initial merge of selected contrib mesh tools to one menu system
authorBrendon Murphy <meta.androcto1@gmail.com>
Mon, 1 Oct 2012 13:42:24 +0000 (13:42 +0000)
committerBrendon Murphy <meta.androcto1@gmail.com>
Mon, 1 Oct 2012 13:42:24 +0000 (13:42 +0000)
12 files changed:
mesh_extra_tools/__init__.py [new file with mode: 0644]
mesh_extra_tools/face_inset_fillet.py [new file with mode: 0644]
mesh_extra_tools/mesh_bevel_round.py [moved from mesh_bevel_round.py with 99% similarity]
mesh_extra_tools/mesh_bevel_witold.py [new file with mode: 0644]
mesh_extra_tools/mesh_bump.py [moved from mesh_multi_extrude/mesh_bump.py with 89% similarity]
mesh_extra_tools/mesh_extras.py [moved from mesh_multi_extrude/mesh_extras.py with 99% similarity]
mesh_extra_tools/mesh_filletplus.py [new file with mode: 0644]
mesh_extra_tools/mesh_mextrude_plus.py [moved from mesh_multi_extrude/mesh_mextrude_plus.py with 96% similarity]
mesh_extra_tools/mesh_normal_smooth.py [moved from mesh_normal_smooth.py with 93% similarity]
mesh_extra_tools/mesh_polyredux.py [new file with mode: 0644]
mesh_extra_tools/mesh_vertex_chamfer.py [moved from mesh_vertex_chamfer.py with 88% similarity]
mesh_multi_extrude/__init__.py [deleted file]

diff --git a/mesh_extra_tools/__init__.py b/mesh_extra_tools/__init__.py
new file mode 100644 (file)
index 0000000..fd4e64a
--- /dev/null
@@ -0,0 +1,163 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+# Contributed to by
+# meta-androcto #
+
+bl_info = {
+    "name": "Extra Tools",
+    "author": "various",
+    "version": (0, 1),
+    "blender": (2, 6, 4),
+    "location": "View3D > Toolbar and View3D > Specials (W-key)",
+    "description": "Add extra mesh edit tools",
+    "warning": "",
+    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
+        "Scripts",
+    "tracker_url": "https://projects.blender.org/tracker/index.php?"\
+        "func=detail&aid=32711",
+    "category": "Mesh"}
+
+
+if "bpy" in locals():
+    import imp
+    imp.reload(mesh_bump)
+    imp.reload(face_inset_fillet)
+    imp.reload(mesh_bevel_witold)
+    imp.reload(mesh_filletplus)
+    imp.reload(mesh_normal_smooth)
+    imp.reload(mesh_polyredux)
+    imp.reload(mesh_vertex_chamfer)
+    imp.reload(mesh_mextrude_plus)
+    imp.reload(mesh_bevel_round)
+
+else:
+    from . import mesh_bump
+    from . import face_inset_fillet
+    from . import mesh_bevel_witold
+    from . import mesh_filletplus
+    from . import mesh_normal_smooth
+    from . import mesh_polyredux
+    from . import mesh_vertex_chamfer
+    from . import mesh_mextrude_plus
+    from . import mesh_bevel_round
+
+import bpy
+
+
+class VIEW3D_MT_edit_mesh_extras(bpy.types.Menu):
+    # Define the "Extras" menu
+    bl_idname = "VIEW3D_MT_edit_mesh_extras"
+    bl_label = "Extra Tools"
+
+    def draw(self, context):
+        layout = self.layout
+        layout.operator_context = 'INVOKE_REGION_WIN'
+        layout.operator("mesh.bump",
+            text="Inset Extrude Bump")
+        layout.operator("fif.op0_id",
+            text="Face Inset Fillet")
+        layout.operator("mesh.mbevel",
+            text="Edge Bevel")
+        layout.operator("f.op0_id",
+            text="Edge Fillet Plus")
+        layout.operator("normal.smooth",
+            text="Normal Smooth")
+        layout.operator("object.mextrude",
+            text="Multi Extrude")
+        layout.operator("mesh.polyredux",
+            text="Poly Redux")
+        layout.operator("mesh.vertex_chamfer",
+            text="Vertex Chamfer")
+        layout.operator("mesh.bevel_round",
+            text="Bevel Round")
+
+
+class ExtrasPanel(bpy.types.Panel):
+    bl_label = 'Mesh Extra Tools'
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        row = layout.split(0.80)
+        row.operator('mesh.bump', text = 'Inset Bump')
+        row.operator('help.bump', text = '?')
+        row = layout.split(0.80)
+        row.operator('fif.op0_id', text = 'Face Inset Fillet')
+        row.operator('help.face_inset', text = '?')
+        row = layout.split(0.80)
+        row.operator('mesh.bevel_round', text = 'Bevel Round')
+        row.operator('help.bevelround', text = '?')
+        row = layout.split(0.80)
+        row.operator('mesh.mbevel', text = 'Edge Bevel')
+        row.operator('help.edge_bevel', text = '?')
+        row = layout.split(0.80)
+        row.operator('f.op0_id', text = 'Edge Fillet plus')
+        row.operator('f.op1_id', text = '?')
+        row = layout.split(0.80)
+        row.operator('normal.smooth', text = 'Normal Smooth')
+        row.operator('help.normal_smooth', text = '?')
+        row = layout.split(0.80)
+        row.operator('mesh.polyredux', text = 'Poly Redux')
+        row.operator('help.polyredux', text = '?')
+        row = layout.split(0.80)
+        row.operator('mesh.vertex_chamfer', text = 'Vertex Chamfer')
+        row.operator('help.vertexchamfer', text = '?')
+        row = layout.split(0.80)
+        row.operator('object.mextrude', text = 'Multi Face Extrude')
+        row.operator('help.mextrude', text = '?')
+
+
+# Multi Extrude Panel
+
+class ExtrudePanel(bpy.types.Panel):
+    bl_label = 'Multi Extrude Plus'
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        prop = layout.operator("wm.context_set_value", text="Face Select",
+            icon='FACESEL')
+        prop.value = "(False, False, True)"
+        prop.data_path = "tool_settings.mesh_select_mode"
+        layout.operator('object.mextrude')
+        layout.operator('mesh.bump')
+        layout.operator('object.mesh2bones')
+# Define "Extras" menu
+def menu_func(self, context):
+    self.layout.menu("VIEW3D_MT_edit_mesh_extras", icon="PLUGIN")
+
+
+def register():
+    bpy.utils.register_module(__name__)
+
+    # Add "Extras" menu to the "Add Mesh" menu
+    bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)
+
+
+def unregister():
+    bpy.utils.unregister_module(__name__)
+
+    # Remove "Extras" menu from the "Add Mesh" menu.
+    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
+
+if __name__ == "__main__":
+    register()
diff --git a/mesh_extra_tools/face_inset_fillet.py b/mesh_extra_tools/face_inset_fillet.py
new file mode 100644 (file)
index 0000000..2a1b307
--- /dev/null
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+
+# ------ ------
+bl_info = {
+    'name': 'face_inset_fillet',
+    'author': '',
+    'version': (0, 1, 7),
+    'blender': (2, 6, 3),
+    'api': 46962,
+    'location': 'View3D > Tool Shelf',
+    'description': '',
+    'warning': '',
+    'wiki_url': '',
+    'tracker_url': '',
+    'category': 'Mesh' }
+
+# ------ ------
+import bpy
+import bmesh
+from bpy.props import FloatProperty, IntProperty, BoolProperty, EnumProperty
+from math import tan, cos, degrees, radians, sin
+from mathutils import Matrix
+
+# ------ ------
+def edit_mode_out():
+    bpy.ops.object.mode_set(mode = 'OBJECT')
+
+def edit_mode_in():
+    bpy.ops.object.mode_set(mode = 'EDIT')
+
+def a_rot(ang, rp, axis, q):
+    return (Matrix.Rotation(ang, 3, axis) * (q - rp)) + rp
+
+# ------ ------
+def f_(bme, list_0, opp, adj1, n_, out, radius, en0, kp):
+
+    list_del = []
+    for fi in list_0:
+        f = bme.faces[fi]
+        f.select_set(0)
+        list_del.append(f)
+        f.normal_update()
+        list_2 = [v.index for v in f.verts]
+        dict_0 = {}
+        list_1 = []
+        n = len(list_2)
+        for i in range(n):
+            dict_0[i] = []
+            p = (bme.verts[ list_2[i] ].co).copy()
+            p1 = (bme.verts[ list_2[(i - 1) % n] ].co).copy()
+            p2 = (bme.verts[ list_2[(i + 1) % n] ].co).copy()
+            dict_0[i].append(bme.verts[list_2[i]])
+            vec1 = p - p1
+            vec2 = p - p2
+            ang = vec1.angle(vec2)
+            adj = opp / tan(ang * 0.5)
+            h = (adj ** 2 + opp ** 2) ** 0.5
+            if round(degrees(ang)) == 180 or round(degrees(ang)) == 0.0:
+                p6 = a_rot(radians(90), p, vec1, p + ((f.normal).normalized() * opp) if out == True else p - ((f.normal).normalized() * opp))
+                list_1.append(p6)
+            else:
+                p6 = a_rot(-radians(90), p, ((p - (vec1.normalized() * adj)) - (p - (vec2.normalized() * adj))), p + ((f.normal).normalized() * h) if out == True else p - ((f.normal).normalized() * h))
+                list_1.append(p6)
+
+        list_2 = []
+        n1_ = len(list_1)
+        for j in range(n1_):
+            q = list_1[j]
+            q1 = list_1[(j - 1) % n1_]
+            q2 = list_1[(j + 1) % n1_]
+            vec1_ = q - q1
+            vec2_ = q - q2
+            ang_ = vec1_.angle(vec2_)
+            if round(degrees(ang_)) == 180 or round(degrees(ang_)) == 0.0:
+                bme.verts.new(q)
+                bme.verts.index_update()
+                list_2.append(bme.verts[-1])
+                dict_0[j].append(bme.verts[-1])
+            else:
+                opp_ = adj1
+                if radius == False:
+                    h_ = adj1 * (1 / cos(ang_ * 0.5))
+                    d = adj1
+                elif radius == True:
+                    h_ = opp_ / sin(ang_ * 0.5)
+                    d = opp_ / tan(ang_ * 0.5)
+
+                q3 = q - (vec1_.normalized() * d)
+                q4 = q - (vec2_.normalized() * d)
+                rp_ = q - ((q - ((q3 + q4) * 0.5)).normalized() * h_)
+                axis_ = vec1_.cross(vec2_)
+                vec3_ = rp_ - q3
+                vec4_ = rp_ - q4
+                rot_ang = vec3_.angle(vec4_)
+                list_3 = []
+                
+                for o in range(n_ + 1):
+                    q5 = a_rot((rot_ang * o / n_), rp_, axis_, q4)
+                    bme.verts.new(q5)
+                    bme.verts.index_update()
+                    dict_0[j].append(bme.verts[-1])
+                    list_3.append(bme.verts[-1])
+                list_3.reverse()
+                list_2.extend(list_3)
+
+        if out == False:
+            bme.faces.new(list_2)
+            bme.faces.index_update()
+            bme.faces[-1].select_set(1)
+        elif out == True and kp == True:
+            bme.faces.new(list_2)
+            bme.faces.index_update()
+            bme.faces[-1].select_set(1)
+
+        n2_ = len(dict_0)
+        for o in range(n2_):
+            list_a = dict_0[o]
+            list_b = dict_0[(o + 1) % n2_]
+            bme.faces.new( [ list_a[0], list_b[0], list_b[-1], list_a[1] ] )
+            bme.faces.index_update()
+
+        if en0 == 'opt0':
+            for k in dict_0:
+                if len(dict_0[k]) > 2:
+                    bme.faces.new(dict_0[k])
+                    bme.faces.index_update()
+        if en0 == 'opt1':
+            for k_ in dict_0:
+                q_ = dict_0[k_][0]
+                dict_0[k_].pop(0)
+                n3_ = len(dict_0[k_])
+                for kk in range(n3_ - 1):
+                    bme.faces.new( [ dict_0[k_][kk], dict_0[k_][(kk + 1) % n3_], q_ ] )
+                    bme.faces.index_update()
+
+    del_ = [bme.faces.remove(f) for f in list_del]
+    del del_
+
+
+
+# ------ operator 0 ------
+class fif_op0(bpy.types.Operator):
+    bl_idname = 'fif.op0_id'
+    bl_label = 'Face Inset Fillet'
+    bl_description = 'inset selected faces'
+    bl_options = {'REGISTER', 'UNDO'}
+
+    opp = FloatProperty( name = '', default = 0.04, min = 0, max = 100.0, step = 1, precision = 3 )      # inset amount
+    n_ = IntProperty( name = '', default = 4, min = 1, max = 100, step = 1 )      # number of sides
+    adj1 = FloatProperty( name = '', default = 0.04, min = 0.00001, max = 100.0, step = 1, precision = 3 )
+    out = BoolProperty( name = 'Out', default = False )
+    radius = BoolProperty( name = 'Radius', default = False )
+    en0 = EnumProperty( items =( ('opt0', 'Type 1', ''), ('opt1', 'Type 2', '') ), name = '', default = 'opt0' )
+    kp = BoolProperty( name = 'Keep face', default = False )
+    
+    def draw(self, context):
+        layout = self.layout
+        box = layout.box()
+        box.prop(self, 'en0', text = 'Corner type')
+        row0 = box.row(align = True)
+        row0.prop(self, 'out')
+        if self.out == True:
+            row0.prop(self, 'kp')
+        row = box.split(0.40, align = True)
+        row.label('Inset amount:')
+        row.prop(self, 'opp')
+        row1 = box.split(0.60, align = True)
+        row1.label('Number of sides:')
+        row1.prop(self, 'n_', slider = True)
+        box.prop(self, 'radius')
+        row2 = box.split(0.40, align = True)
+        if self.radius == True:
+            row2.label('Radius:')
+        else:
+            row2.label('Distance:')
+        row2.prop(self, 'adj1')
+
+    def execute(self, context):
+        opp = self.opp
+        n_ = self.n_
+        adj1 = self.adj1
+        out = self.out
+        radius = self.radius
+        en0 = self.en0
+        kp = self.kp
+
+        edit_mode_out()
+        ob_act = context.active_object
+        bme = bmesh.new()
+        bme.from_mesh(ob_act.data)
+        
+        list_0 = [ f.index for f in bme.faces if f.select and f.is_valid ]
+
+        if len(list_0) == 0:
+            self.report({'INFO'}, 'No faces selected unable to continue.')
+            edit_mode_in()
+            return {'CANCELLED'}
+        elif len(list_0) != 0:
+            f_(bme, list_0, opp, adj1, n_, out, radius, en0, kp)
+
+        bme.to_mesh(ob_act.data)
+        edit_mode_in()
+        return {'FINISHED'}
+
+class inset_help(bpy.types.Operator):
+       bl_idname = 'help.face_inset'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Select A face or Faces & inset.')
+               layout.label('Inset on a per face basis')
+               layout.label('Inset square, circle or outside.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+'''
+# ------ ------
+class_list = [ fif_op0, fif_p0 ]
+
+# ------ register ------
+def register():
+    for c in class_list:
+        bpy.utils.register_class(c)
+
+# ------ unregister ------
+def unregister():
+    for c in class_list:
+        bpy.utils.unregister_class(c)
+
+# ------ ------
+if __name__ == "__main__":
+    register()
+'''
\ No newline at end of file
similarity index 99%
rename from mesh_bevel_round.py
rename to mesh_extra_tools/mesh_bevel_round.py
index d9a02d6090eb177b7eb229ab064e4cd5b6fd5133..78c86e1435e84d3fda74fc43a29b93957c8f3f36 100644 (file)
@@ -127,6 +127,22 @@ class BevelRound(bpy.types.Operator):
         bpy.ops.object.editmode_toggle()
         bpy.ops.object.editmode_toggle()
 
+class rbevel_help(bpy.types.Operator):
+       bl_idname = 'help.bevelround'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Select edges or faces to bevel with the option of rounded bevels.')
+               layout.label('best used on flat edges & simple edgeflow')
+               layout.label('may error if vert joins multiple edges/complex edge selection.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 400)
 
 class BevelRoundAlgo(object):
     def __init__(self, bm, kind, offset, num_seg, strict, round):
diff --git a/mesh_extra_tools/mesh_bevel_witold.py b/mesh_extra_tools/mesh_bevel_witold.py
new file mode 100644 (file)
index 0000000..dccde77
--- /dev/null
@@ -0,0 +1,196 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+'''
+Bevel add-on
+'''
+#--- ### Header
+bl_info = {
+    "name": "Bevel witold",
+    "author": "Witold Jaworski",
+    "version": (1, 2, 0),
+    "blender": (2, 5, 7),
+    "api": 36147,
+    "location": "View3D >Specials (W-key)",
+    "category": "Mesh",
+    "description": "Bevels selected edges",
+    "warning": "",
+    "wiki_url": "http://airplanes3d.net/scripts-252_e.xml",
+    "tracker_url": "http://airplanes3d.net/track-252_e.xml"
+    }
+#--- ### Change log
+#2011-08-08 Witold Jaworski: added "Vertex only" feature
+#--- ### Imports
+import bpy
+from bpy.utils import register_module, unregister_module
+from bpy.props import FloatProperty, IntProperty, BoolProperty
+from math import log10, floor, pow
+#--- ### Constants
+DEBUG = 0 #Debug flag - just some text printed on the console...
+#--- ### Core operation
+def bevel(obj, width, use_vertices):
+    """Bevels selected edges of the mesh
+       Arguments:
+            @obj (Object):         an object with a mesh. 
+                                   It should have some edges selected
+            @width (float):        width of the bevel
+            @use_vertices (bool):  True, when bevel only vertices. False otherwise
+       This function should be called in the Edit Mode, only!
+    """    
+    #
+    #edge = bpy.types.MeshEdge
+    #obj = bpy.types.Object
+    #bevel = bpy.types.BevelModifier
+
+    bpy.ops.object.editmode_toggle() #switch into OBJECT mode
+    #adding the Bevel modifier
+    bpy.ops.object.modifier_add(type = 'BEVEL')  
+    bevel = obj.modifiers[-1] #the new modifier is always added at the end
+    bevel.limit_method = 'WEIGHT'
+    bevel.edge_weight_method = 'LARGEST'
+    bevel.width = width
+    bevel.use_only_vertices = use_vertices
+    #moving it up, to the first position on the modifier stack:
+    while obj.modifiers[0] != bevel:
+        bpy.ops.object.modifier_move_up(modifier = bevel.name)
+        
+    for elm in (obj.data.vertices if use_vertices else obj.data.edges):
+        if elm.select:
+            elm.bevel_weight = 1.0 
+    
+    bpy.ops.object.modifier_apply(apply_as = 'DATA', modifier = bevel.name)
+    
+    #clean up after applying our modifier: remove bevel weights:
+    for elm in (obj.data.vertices if use_vertices else obj.data.edges):
+        if elm.select:
+            elm.bevel_weight = 0.0 
+            
+    bpy.ops.object.editmode_toggle() #switch back into EDIT_MESH mode
+
+class bevel_help(bpy.types.Operator):
+       bl_idname = 'help.edge_bevel'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Select A edge or edges & bevel.')
+               layout.label('Simple Straight bevel')
+               layout.label('or Select 2 or more verices & bevel.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+#--- ### Operator
+class Bevel(bpy.types.Operator):
+    ''' Bevels selected edges of the mesh'''
+    bl_idname = "mesh.mbevel" #it is not named mesh.bevel, to not confuse with the standard bevel in the future...
+    bl_label = "Edge Bevel"
+    bl_description = "Bevels selected edges"
+    bl_options = {'REGISTER', 'UNDO'} #Set this options, if you want to update  
+    #                                  parameters of this operator interactively 
+    #                                  (in the Tools pane) 
+    #--- parameters
+    use_vertices = BoolProperty(name="Only Vertices", description="Bevel vertices (corners), not edges", default = False)
+    
+    width = FloatProperty(name="Width", description="Bevel width value (it is multiplied by 10^Exponent)", 
+                          subtype = 'DISTANCE', default = 0.1, min = 0.0, 
+                                                    step = 1, precision = 2)
+    exponent = IntProperty(name="Exponent", description="Order of magnitude of the bevel width (the power of 10)", default = 0)
+    
+    use_scale = BoolProperty(name="Use object scale", description="Multiply bevel width by the scale of this object", default = False)
+
+    #--- other fields
+    LAST_VERT_NAME = "mesh.mbevel.last_vert" #the name of the custom scene property 
+    LAST_WIDTH_NAME = "mesh.mbevel.last_width" #the name of the custom scene property 
+    LAST_EXP_NAME = "mesh.mbevel.last_exponent" #the name of the custom scene property 
+    LAST_SCALE_NAME = "mesh.mbevel.last_scale" #scale Bevel width by the object scale 
+    #--- Blender interface methods
+    @classmethod
+    def poll(cls,context):
+        return (context.mode == 'EDIT_MESH')
+
+    def invoke(self, context, event):
+        #input validation: 
+        # 1. Require single-user mesh (modifiers cannot be applied to the multi-user ones):
+        obj = context.object
+        if obj.data.users > 1:
+            self.report(type='ERROR', message="Make this mesh single-user, first")
+            return {'CANCELLED'}
+        # 2. is there anything selected?
+        self.use_vertices = context.scene.get(self.LAST_VERT_NAME, self.use_vertices)
+
+        bpy.ops.object.editmode_toggle()
+        
+        if self.use_vertices :
+            selected = list(filter(lambda e: e.select, context.object.data.vertices))
+        else:
+            selected = list(filter(lambda e: e.select, context.object.data.edges))
+            
+        bpy.ops.object.editmode_toggle()
+            
+        if len(selected) > 0:
+            self.use_scale = context.object.get(self.LAST_SCALE_NAME, self.use_scale)
+            
+            #setup the default width, to avoid user surprises :)
+            def_exp = floor(log10(obj.dimensions.length)) #heuristic: default width exponent is derived from the object size...
+            self.exponent = context.scene.get(self.LAST_EXP_NAME, def_exp) #Let's read the last used value, stored in the scene...
+            larger = def_exp - self.exponent #How larger/smaller is actual object, comparing to the last used value?
+            if larger <= 1 and larger >= 0: #OK, this object has similar size to the previous one...
+                self.width = context.scene.get(self.LAST_WIDTH_NAME, self.width)
+            else: #the previous bevel size would be too small or too large - revert to defaults:
+                self.width = 0.1 #10% of the object order of magnitude
+                self.exponent = def_exp #the order of magnitude
+            #parameters adjusted, run the command!    
+            return self.execute(context)
+        else:
+            self.report(type='ERROR', message="Nothing is selected")
+            return {'CANCELLED'}
+        
+    def execute(self,context):
+        #calculate the bevel width, for this object size and scale
+        width = self.width*pow(10,self.exponent)
+        if not self.use_scale : width /= max(context.object.scale)
+        #call the main function:
+        bevel(context.object,width, self.use_vertices)
+        #save the last used parameters:
+        context.scene[self.LAST_VERT_NAME] = self.use_vertices
+        context.scene[self.LAST_WIDTH_NAME] = self.width
+        context.scene[self.LAST_EXP_NAME] = self.exponent
+        context.object[self.LAST_SCALE_NAME] = self.use_scale
+        return {'FINISHED'}
+
+def menu_draw(self, context):
+    self.layout.operator_context = 'INVOKE_REGION_WIN'
+    self.layout.operator(Bevel.bl_idname, "Bevel_Witold")
+    
+#--- ### Register
+def register():
+    register_module(__name__)
+    bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_draw)
+    
+def unregister():
+    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_draw)
+    unregister_module(__name__)
+    
+#--- ### Main code    
+if __name__ == '__main__':
+    register()
+    
+if DEBUG > 0: print("mesh_bevel.py loaded!")
similarity index 89%
rename from mesh_multi_extrude/mesh_bump.py
rename to mesh_extra_tools/mesh_bump.py
index 2bcc53b29ee927f2f8b53292269b9ae07ef2be6a..9b11bb541d9ee6dec2e4966a8f4cccef2ed2f03a 100644 (file)
@@ -149,9 +149,9 @@ class Bump():
        
 
 class Bump_init(bpy.types.Operator):
-       """Bump by extruding and moving/rotating/scaling multiple times"""
+       '''Bump by extruding and moving/rotating/scaling multiple times'''
        bl_idname = 'mesh.bump'
-       bl_label = 'Bump'
+       bl_label = 'Inset Extrude Bump'
        bl_options = {'REGISTER', 'UNDO'}
        
        # The falloffs we use
@@ -178,8 +178,23 @@ class Bump_init(bpy.types.Operator):
                BUMP = Bump(context, self.type, self.scale, self.steps) 
                return {'FINISHED'}
 
-               
+class bump_help(bpy.types.Operator):
+       bl_idname = 'help.bump'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of Faces ')
+               layout.label('Choose from the bump types in the menu.')
+               layout.label('Keep extrusions small to prevent overlapping.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
 
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)           
+'''
 def menu_func(self, context):
        self.layout.operator(Bump_init.bl_idname, text="Bump")
 
@@ -193,3 +208,4 @@ def unregister():
 
 if __name__ == "__main__":
        register()
+'''
\ No newline at end of file
similarity index 99%
rename from mesh_multi_extrude/mesh_extras.py
rename to mesh_extra_tools/mesh_extras.py
index 8b434c1d570641363e06cf4ca3abc37ae6f829cf..49ddd446829b36b944d4f930dee505b33b3406a6 100644 (file)
@@ -271,4 +271,4 @@ def contains_selected_item(items):
 
        
        
-               
+               
\ No newline at end of file
diff --git a/mesh_extra_tools/mesh_filletplus.py b/mesh_extra_tools/mesh_filletplus.py
new file mode 100644 (file)
index 0000000..ad106f0
--- /dev/null
@@ -0,0 +1,380 @@
+# -*- coding: utf-8 -*-
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+
+# ------ ------
+bl_info = {
+       'name': 'FilletPlus',
+       'author': 'Gert De Roost - original by zmj100',
+       'version': (0, 4, 2),
+       'blender': (2, 6, 1),
+       'api': 43085,
+       'location': 'View3D > Tool Shelf',
+       'description': '',
+       'warning': '',
+       'wiki_url': '',
+       'tracker_url': '',
+       'category': 'Mesh' }
+
+# ------ ------
+import bpy
+from bpy.props import FloatProperty, IntProperty, BoolProperty
+import bmesh
+from mathutils import Matrix
+from math import cos, pi, degrees, sin, tan
+
+
+def list_clear_(l):
+       l[:] = []
+       return l
+
+def get_adj_v_(list_):
+               tmp = {}
+               for i in list_:
+                               try:                     tmp[i[0]].append(i[1])
+                               except KeyError: tmp[i[0]] = [i[1]]
+                               try:                     tmp[i[1]].append(i[0])
+                               except KeyError: tmp[i[1]] = [i[0]]
+               return tmp
+
+# ------ ------
+class f_buf():
+       an = 0
+
+# ------ ------
+def f_(list_0, startv, vertlist, face, adj, n, out, flip, radius):
+
+       dict_0 = get_adj_v_(list_0)
+       list_1 = [[dict_0[i][0], i, dict_0[i][1]] for i in dict_0 if (len(dict_0[i]) == 2)][0]
+       list_3 = []
+       for elem in list_1:
+               list_3.append(bm.verts[elem])
+       list_2 = []
+
+       p_ = list_3[1]
+       p = (list_3[1].co).copy()
+       p1 = (list_3[0].co).copy()
+       p2 = (list_3[2].co).copy()
+
+       vec1 = p - p1
+       vec2 = p - p2
+
+       ang = vec1.angle(vec2, any)
+       f_buf.an = round(degrees(ang))
+
+       # -- -- -- --
+       if f_buf.an == 180 or f_buf.an == 0.0:
+               return
+
+       # -- -- -- --
+       opp = adj
+
+       if radius == False:
+               h = adj * (1 / cos(ang * 0.5))
+               adj_ = adj
+       elif radius == True:
+               h = opp / sin(ang * 0.5)
+               adj_ = opp / tan(ang * 0.5)
+
+       p3 = p - (vec1.normalized() * adj_)
+       p4 = p - (vec2.normalized() * adj_)
+       rp = p - ((p - ((p3 + p4) * 0.5)).normalized() * h)
+
+       vec3 = rp - p3
+       vec4 = rp - p4
+
+       axis = vec1.cross(vec2)
+
+       if out == False:
+               if flip == False:
+                       rot_ang = vec3.angle(vec4)
+               elif flip == True:
+                       rot_ang = vec1.angle(vec2)
+       elif out == True:
+               rot_ang = (2 * pi) - vec1.angle(vec2)
+
+       for j in range(n + 1):
+               new_angle = rot_ang * j / n
+               mtrx = Matrix.Rotation(new_angle, 3, axis)
+               if out == False:
+                       if flip == False:
+                               tmp = p4 - rp
+                               tmp1 = mtrx * tmp
+                               tmp2 = tmp1 + rp
+                       elif flip == True:
+                               p3 = p - (vec1.normalized() * opp)
+                               tmp = p3 - p
+                               tmp1 = mtrx * tmp
+                               tmp2 = tmp1 + p
+               elif out == True:
+                       p4 = p - (vec2.normalized() * opp)
+                       tmp = p4 - p
+                       tmp1 = mtrx * tmp
+                       tmp2 = tmp1 + p
+
+               v = bm.verts.new(tmp2)
+               list_2.append(v)
+               
+       if flip == True:
+               list_3[1:2] = list_2
+       else:
+               list_2.reverse()
+               list_3[1:2] = list_2
+
+       list_clear_(list_2)
+
+       n1 = len(list_3)
+       for t in range(n1 - 1):
+               bm.edges.new([list_3[t], list_3[(t + 1) % n1]])
+
+               v = bm.verts.new(p)
+               bm.edges.new([v, p_])
+       
+       if face != None:
+               for l in face.loops:
+                       if l.vert == list_3[0]:
+                               startl = l
+                               break
+               vertlist2 = []
+               if startl.link_loop_next.vert == startv:
+                       l = startl.link_loop_prev
+                       while len(vertlist) > 0:
+                               vertlist2.insert(0, l.vert)
+                               vertlist.pop(vertlist.index(l.vert))
+                               l = l.link_loop_prev
+               else:
+                       l = startl.link_loop_next
+                       while len(vertlist) > 0:
+                               vertlist2.insert(0, l.vert)
+                               vertlist.pop(vertlist.index(l.vert))
+                               l = l.link_loop_next
+               for v in list_3:
+                       vertlist2.append(v)
+               bm.faces.new(vertlist2)
+               
+       bm.verts.remove(startv)
+       list_3[1].select = 1
+       list_3[-2].select = 1
+       bm.edges.get([list_3[0], list_3[1]]).select = 1
+       bm.edges.get([list_3[-1], list_3[-2]]).select = 1
+       bm.verts.index_update()
+       bm.edges.index_update()
+       bm.faces.index_update()
+       
+       me.update(calc_edges = True, calc_tessface=True)
+       
+       
+
+def do_filletplus(pair):
+       
+       global inaction
+       global flip
+       
+       
+       list_0 = [list([e.verts[0].index, e.verts[1].index]) for e in pair]
+
+       vertset = set([])
+       vertset.add(bm.verts[list_0[0][0]])
+       vertset.add(bm.verts[list_0[0][1]])
+       vertset.add(bm.verts[list_0[1][0]])
+       vertset.add(bm.verts[list_0[1][1]])
+       
+       v1, v2, v3 = vertset
+
+       if len(list_0) != 2:
+               self.report({'INFO'}, 'Two adjacent edges must be selected.')
+               return
+       else:
+               inaction = 1
+               vertlist = []
+               found = 0
+               for f in v1.link_faces:
+                       if v2 in f.verts and v3 in f.verts:
+                               found = 1
+               if not(found):
+                       for v in [v1, v2, v3]:
+                               if v.index in list_0[0] and v.index in list_0[1]:
+                                       startv = v
+                       face = None
+               else:
+                       for f in v1.link_faces:
+                               if v2 in f.verts and v3 in f.verts:
+                                       for v in f.verts:
+                                               if not(v in vertset):
+                                                       vertlist.append(v)
+                                               if v in vertset and v.link_loops[0].link_loop_prev.vert in vertset and v.link_loops[0].link_loop_next.vert in vertset:
+                                                       startv = v
+                                       face = f
+               if out == True:
+                       flip = False
+               f_(list_0, startv, vertlist, face, adj, n, out, flip, radius)
+               
+
+'''
+
+# ------ panel 0 ------
+class f_p0(bpy.types.Panel):
+       bl_space_type = 'VIEW_3D'
+       bl_region_type = 'TOOLS'
+       #bl_idname = 'f_p0_id'                                                                                            
+       bl_label = 'Fillet'
+       bl_context = 'mesh_edit'
+
+       def draw(self, context):
+               layout = self.layout
+               
+               row = layout.split(0.80)
+               row.operator('f.op0_id', text = 'Fillet plus')
+               row.operator('f.op1_id', text = '?')
+'''
+# ------ operator 0 ------
+class f_op0(bpy.types.Operator):
+       bl_idname = 'f.op0_id'
+       bl_label = 'Fillet'
+       bl_description = 'Fillet ajoining edges'
+       bl_options = {'REGISTER', 'UNDO'}
+
+       adj = FloatProperty( name = '', default = 0.1, min = 0.00001, max = 100.0, step = 1, precision = 3 )
+       n = IntProperty( name = '', default = 3, min = 1, max = 100, step = 1 )
+       out = BoolProperty( name = 'Outside', default = False )
+       flip = BoolProperty( name = 'Flip', default = False )
+       radius = BoolProperty( name = 'Radius', default = False )
+       
+       
+       @classmethod
+       def poll(cls, context):
+               obj = context.active_object
+               return (obj and obj.type == 'MESH' and context.mode == 'EDIT_MESH')
+       
+       def draw(self, context):
+               layout = self.layout
+
+               if f_buf.an == 180 or f_buf.an == 0.0:
+                       layout.label('Info:')
+                       layout.label('Angle equal to 0 or 180,')
+                       layout.label('can not fillet.')
+               else:
+                       layout.prop(self, 'radius')
+                       if self.radius == True:
+                               layout.label('Radius:')
+                       elif self.radius == False:
+                               layout.label('Distance:')
+                       layout.prop(self, 'adj')
+                       layout.label('Number of sides:')
+                       layout.prop(self, 'n', slider = True)
+                       if self.n > 1:
+                               row = layout.row(align = False)
+                               row.prop(self, 'out')
+                               if self.out == False:
+                                       row.prop(self, 'flip')
+
+       def execute(self, context):
+
+               global inaction
+               global bm, me, adj, n, out, flip, radius
+
+               adj = self.adj
+               n = self.n
+               out = self.out
+               flip = self.flip
+               radius = self.radius
+               
+               inaction = 0
+
+               ob_act = context.active_object
+               me = ob_act.data
+               bm = bmesh.from_edit_mesh(me)
+#              e_mode = bpy.context.tool_settings.mesh_select_mode
+               
+               done = 1
+               while done:
+                       tempset = set([])
+                       for v in bm.verts:
+                               if v.select:
+                                       tempset.add(v)
+                       done = 0                
+                       for v in tempset:
+                               cnt = 0
+                               edgeset = set([])
+                               for e in v.link_edges:
+                                       if e.select:
+                                               edgeset.add(e)
+                                               cnt += 1
+                               if cnt == 2:
+                                       do_filletplus(edgeset)
+                                       done = 1
+                                       break
+                                       #return {'FINISHED'}
+                               if done:
+                                       break
+                                       
+               if inaction == 1:
+                       bpy.ops.mesh.select_all(action="DESELECT")
+                       for v in bm.verts:
+                               if len(v.link_edges) == 0:
+                                       bm.verts.remove(v)
+                       bpy.ops.object.editmode_toggle()
+                       bpy.ops.object.editmode_toggle()
+                       return {'FINISHED'}
+               else:
+                       return {'CANCELLED'}
+
+# ------ operator 1 ------
+class f_op1(bpy.types.Operator):
+       bl_idname = 'f.op1_id'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Select two adjacent edges and press Fillet button.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 400)
+
+# ------ operator 2 ------
+class f_op2(bpy.types.Operator):
+       bl_idname = 'f.op2_id'
+       bl_label = ''
+
+       def execute(self, context):
+               bpy.ops.f.op1_id('INVOKE_DEFAULT')
+               return {'FINISHED'}
+'''
+# ------ ------
+class_list = [ f_op0, f_op1, f_op2, f_p0]
+
+# ------ register ------
+def register():
+       for c in class_list:
+               bpy.utils.register_class(c)
+
+# ------ unregister ------
+def unregister():
+       for c in class_list:
+               bpy.utils.unregister_class(c)
+
+# ------ ------
+if __name__ == "__main__":
+       register()
+'''
\ No newline at end of file
similarity index 96%
rename from mesh_multi_extrude/mesh_mextrude_plus.py
rename to mesh_extra_tools/mesh_mextrude_plus.py
index 672142c66ee3209e236ce8f65b1034077d30ad7b..c7bccd086e8b927dbe62f74052216b1eb649b0d9 100644 (file)
@@ -182,6 +182,23 @@ class MExtrude(bpy.types.Operator):
             self.report({'INFO'}, 'Select one or more faces...')
         return{'FINISHED'}
 
+class mextrude_help(bpy.types.Operator):
+       bl_idname = 'help.mextrude'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of Faces.')
+               layout.label('Extrude, rotate extrusions & more.')
+               layout.label('For rigging capabilities, see Multi Extrude panel.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+               
 class BB(bpy.types.Operator):
     bl_idname = 'object.mesh2bones'
     bl_label = 'Create Armature'
@@ -348,7 +365,7 @@ class BB(bpy.types.Operator):
         scn.objects.active = obj
         volver(obj, copia, om, msm, msv)
         return{'FINISHED'}
-
+'''
 def register():
     bpy.utils.register_class(MExtrude)
 
@@ -362,3 +379,4 @@ def unregister():
 
 if __name__ == '__main__':
     register()
+'''
\ No newline at end of file
similarity index 93%
rename from mesh_normal_smooth.py
rename to mesh_extra_tools/mesh_normal_smooth.py
index 6c5abde875fe2bc73f132e735f5050ab061eb55b..5fb16fad40ed5daf2ee0d741438c599b4764db74 100644 (file)
@@ -157,6 +157,22 @@ def normal_smooth(context):
             
     bpy.ops.object.mode_set(mode='EDIT')
 
+class nsmooth_help(bpy.types.Operator):
+       bl_idname = 'help.normal_smooth'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Select A vertex or group of verts.')
+               layout.label('Smooth the vertex position based on the normals')
+
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
     
 class NormalSmooth(bpy.types.Operator):
     """Smoothes verticle position based on vertex normals"""
diff --git a/mesh_extra_tools/mesh_polyredux.py b/mesh_extra_tools/mesh_polyredux.py
new file mode 100644 (file)
index 0000000..96b9bba
--- /dev/null
@@ -0,0 +1,386 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Campbell J Barton
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+bl_info = {
+       "name": "PolyRedux",
+       "author": "Campbell J Barton - updated by Gert De Roost",
+       "version": (2, 0, 4),
+       "blender": (2, 6, 3),
+       "location": "View3D > Tools",
+       "description": "predictable mesh simplifaction maintaining face loops",
+       "warning": "",
+       "wiki_url": "",
+       "tracker_url": "",
+       "category": "Mesh"}
+       
+
+if "bpy" in locals():
+    import imp
+
+import bpy
+import bmesh
+import time
+
+
+class PolyRedux(bpy.types.Operator):
+       bl_idname = "mesh.polyredux"
+       bl_label = "PolyRedux"
+       bl_description = "predictable mesh simplifaction maintaining face loops"
+       bl_options = {"REGISTER", "UNDO"}
+       
+
+       @classmethod
+       def poll(cls, context):
+               obj = context.active_object
+               return (obj and obj.type == 'MESH')
+
+       def invoke(self, context, event):
+               
+               scn = bpy.context.scene
+               
+               self.save_global_undo = bpy.context.user_preferences.edit.use_global_undo
+               bpy.context.user_preferences.edit.use_global_undo = False
+               
+               do_polyredux(self)
+               
+               return {'FINISHED'}
+
+class redux_help(bpy.types.Operator):
+       bl_idname = 'help.polyredux'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection of verts or polygons to reduce.')
+               layout.label('works on whole mesh')
+               layout.label('Single operation, no parameters.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+'''
+def panel_func(self, context):
+       
+       scn = bpy.context.scene
+       self.layout.label(text="PolyRedux:")
+       self.layout.operator("mesh.polyredux", text="Poly Redux")
+
+
+def register():
+       bpy.utils.register_module(__name__)
+       bpy.types.VIEW3D_PT_tools_meshedit.append(panel_func)
+
+
+def unregister():
+       bpy.utils.unregister_module(__name__)
+       bpy.types.VIEW3D_PT_tools_meshedit.remove(panel_func)
+
+
+if __name__ == "__main__":
+       register()
+
+
+'''
+
+def my_mesh_util():
+       bm_verts = bm.verts
+       
+       vert_faces = [ [] for v in bm_verts]
+       vert_faces_corner = [ [] for v in bm_verts]
+       
+       
+       # Ignore topology where there are not 2 faces connected to an edge.
+       edge_count = {}
+       for f in bm.faces:
+               for edge in f.edges:
+                       edkey = (edge.verts[0].index, edge.verts[1].index)
+                       try:
+                               edge_count[edkey] += 1
+                       except:
+                               edge_count[edkey]  = 1
+                               
+       for edkey, count in edge_count.items():
+               
+               # Ignore verts that connect to edges with more than 2 faces.
+               if count != 2:
+                       vert_faces[edkey[0]] = None
+                       vert_faces[edkey[1]] = None
+       # Done
+       
+       
+       
+       def faces_set_verts(face_ls):
+               unique_verts = set()
+               for f in face_ls:
+                       for v in f.verts:
+                               unique_verts.add(v.index)
+               return unique_verts
+       
+       for f in bm.faces:
+               for corner, v in enumerate(f.verts):
+                       i = v.index
+                       if vert_faces[i] != None:
+                               vert_faces[i].append(f)
+                               vert_faces_corner[i].append(corner)
+       
+       grid_data_ls = []
+       
+       for vi, face_ls in enumerate(vert_faces):
+               if face_ls != None:
+                       if len(face_ls) == 4:
+                               if face_ls[0].select and face_ls[1].select and face_ls[2].select and face_ls[3].select:                                 
+                                       # Support triangles also
+                                       unique_vert_count = len(faces_set_verts(face_ls))
+                                       quads = 0
+                                       for f in face_ls:
+                                               if len(f.verts) ==4:
+                                                       quads += 1
+                                       if unique_vert_count==5+quads: # yay we have a grid
+                                               grid_data_ls.append( (vi, face_ls) )
+                       
+                       elif len(face_ls) == 3:
+                               if face_ls[0].select and face_ls[1].select and face_ls[2].select:
+                                       unique_vert_count = len(faces_set_verts(face_ls))
+                                       if unique_vert_count==4: # yay we have 3 triangles to make into a bigger triangle
+                                               grid_data_ls.append( (vi, face_ls) )
+                               
+       
+       
+       # Now sort out which grid faces to use
+       
+       
+       # This list will be used for items we can convert, vertex is key, faces are values
+       grid_data_dict = {}
+       
+       if not grid_data_ls:
+               print ("doing nothing")
+               return
+       
+       # quick lookup for the opposing corner of a qiad
+       quad_diag_mapping = 2,3,0,1
+       
+       verts_used = [0] * len(bm_verts) # 0 == untouched, 1==should touch, 2==touched
+       verts_used[grid_data_ls[0][0]] = 1 # start touching 1!
+       
+       # From the corner vert, get the 2 edges that are not the corner or its opposing vert, this edge will make a new face
+       quad_edge_mapping = (1,3), (2,0), (1,3), (0,2) # hi-low, low-hi order is intended
+       tri_edge_mapping = (1,2), (0,2), (0,1)
+       
+       done_somthing = True
+       while done_somthing:
+               done_somthing = False
+               grid_data_ls_index = -1
+               
+               for vi, face_ls in grid_data_ls:
+                       grid_data_ls_index += 1
+                       if len(face_ls) == 3:
+                               grid_data_dict[bm.verts[vi]] = face_ls
+                               grid_data_ls.pop( grid_data_ls_index )
+                               break
+                       elif len(face_ls) == 4:
+                               # print vi
+                               if verts_used[vi] == 1:
+                                       verts_used[vi] = 2 # dont look at this again.
+                                       done_somthing = True
+                                       
+                                       grid_data_dict[bm.verts[vi]] = face_ls
+                                       
+                                       # Tag all faces verts as used
+                                       
+                                       for i, f in enumerate(face_ls):
+                                               # i == face index on vert, needed to recall which corner were on.
+                                               v_corner = vert_faces_corner[vi][i]
+                                               fv =f.verts
+                                               
+                                               if len(f.verts) == 4:
+                                                       v_other = quad_diag_mapping[v_corner]
+                                                       # get the 2 other corners
+                                                       corner1, corner2 = quad_edge_mapping[v_corner]
+                                                       if verts_used[fv[v_other].index] == 0:
+                                                               verts_used[fv[v_other].index] = 1 # TAG for touching!
+                                               else:
+                                                       corner1, corner2 = tri_edge_mapping[v_corner]
+                                               
+                                               verts_used[fv[corner1].index] = 2 # Dont use these, they are 
+                                               verts_used[fv[corner2].index] = 2
+                                               
+                                               
+                                       # remove this since we have used it.
+                                       grid_data_ls.pop( grid_data_ls_index )
+                                       
+                                       break
+               
+               if done_somthing == False:
+                       # See if there are any that have not even been tagged, (probably on a different island), then tag them.
+                       
+                       for vi, face_ls in grid_data_ls:
+                               if verts_used[vi] == 0:
+                                       verts_used[vi] = 1
+                                       done_somthing = True
+                                       break
+       
+       
+       # Now we have all the areas we will fill, calculate corner triangles we need to fill in.
+       new_faces = []
+       quad_del_vt_map = (1,2,3), (0,2,3), (0,1,3), (0,1,2)
+       for v, face_ls in grid_data_dict.items():
+               for i, f in enumerate(face_ls):
+                       if len(f.verts) == 4:
+                               # i == face index on vert, needed to recall which corner were on.
+                               v_corner = vert_faces_corner[v.index][i]
+                               v_other = quad_diag_mapping[v_corner]
+                               fv =f.verts
+                               
+                               #print verts_used[fv[v_other].index]
+                               #if verts_used[fv[v_other].index] != 2: # DOSNT WORK ALWAYS
+                               
+                               if 1: # THIS IS LAzY - some of these faces will be removed after adding.
+                                       # Ok we are removing half of this face, add the other half
+                                       
+                                       # This is probably slower
+                                       # new_faces.append( [fv[ii].index for ii in (0,1,2,3) if ii != v_corner ] )
+                                       
+                                       # do this instead
+                                       new_faces.append( (fv[quad_del_vt_map[v_corner][0]].index, fv[quad_del_vt_map[v_corner][1]].index, fv[quad_del_vt_map[v_corner][2]].index) )
+       
+       del grid_data_ls
+       
+       
+       # me.sel = 0
+       def faceCombine4(vi, face_ls):
+               edges = []
+               
+               for i, f in enumerate(face_ls):
+                       fv = f.verts
+                       v_corner = vert_faces_corner[vi][i]
+                       if len(f.verts)==4:     ed = quad_edge_mapping[v_corner]
+                       else:                   ed = tri_edge_mapping[v_corner]
+                       
+                       edges.append( [fv[ed[0]].index, fv[ed[1]].index] )
+               
+               # get the face from the edges 
+               face = edges.pop()
+               while len(face) != 4:
+                       # print len(edges), edges, face
+                       for ed_idx, ed in enumerate(edges):
+                               if face[-1] == ed[0] and (ed[1] != face[0]):
+                                       face.append(ed[1])
+                               elif face[-1] == ed[1] and (ed[0] != face[0]):
+                                       face.append(ed[0])
+                               else:
+                                       continue
+                               
+                               edges.pop(ed_idx) # we used the edge alredy
+                               break
+               
+               return face     
+       
+       for v, face_ls in grid_data_dict.items():
+               vi = v.index
+               if len(face_ls) == 4:
+                       new_faces.append( faceCombine4(vi, face_ls) )
+                       #pass
+               if len(face_ls) == 3: # 3 triangles
+                       face = list(faces_set_verts(face_ls))
+                       face.remove(vi)
+                       new_faces.append( face )
+                       
+       
+       # Now remove verts surounded by 3 triangles
+       
+
+               
+       # print new_edges
+       # me.faces.extend(new_faces, ignoreDups=True)
+       
+       '''
+       faces_remove = []
+       for vi, face_ls in grid_data_dict.items():
+               faces_remove.extend(face_ls)
+       '''
+       
+       orig_facelen = len(bm.faces)
+       
+       orig_faces = list(bm.faces)
+       made_faces = []
+       bpy.ops.mesh.select_all(action="DESELECT")
+       for vertidxs in new_faces:
+               verts = []
+               for idx in vertidxs:
+                       verts.append(bm.verts[idx])
+               verts.append(verts[0])
+               for idx in range(len(verts) - 1):
+                       verts.append(verts[0])
+                       v1 = verts[idx]
+                       v2 = verts[idx + 1]
+                       if bm.edges.get((v1, v2)) == None:
+                               for f in v1.link_faces:
+                                       if f in v2.link_faces:
+                                               bmesh.utils.face_split(f, v1, v2)
+                                               break
+
+       for vert in grid_data_dict.keys():
+               bmesh.utils.face_join(vert.link_faces[:])
+       
+       # me.faces.delete(1, faces_remove)
+       
+       bm.normal_update()
+
+def do_polyredux(self):
+       
+       global bm, me
+       
+       # Gets the current scene, there can be many scenes in 1 blend file.
+       sce = bpy.context.scene
+       
+       # Get the active object, there can only ever be 1
+       # and the active object is always the editmode object.
+       mode = "EDIT"
+       if bpy.context.mode == "OBJECT":
+               mode = "OBJECT"
+               bpy.ops.object.editmode_toggle()
+       ob_act = bpy.context.active_object
+       if not ob_act or ob_act.type != 'MESH':
+               return 
+       me = ob_act.data
+       bm = bmesh.from_edit_mesh(me)
+       
+       t = time.time()
+       
+       # Run the mesh editing function
+       my_mesh_util()
+       me.update(calc_edges=True, calc_tessface=True)
+       bm.free()
+       
+       # Restore editmode if it was enabled
+       if mode == "OBJECT":
+               bpy.ops.object.editmode_toggle()
+       else:
+               bpy.ops.object.editmode_toggle()
+               bpy.ops.object.editmode_toggle()
+       
+       # Timing the script is a good way to be aware on any speed hits when scripting
+       print ('My Script finished in %.2f seconds' % (time.time()-t))
+       
+       
+
similarity index 88%
rename from mesh_vertex_chamfer.py
rename to mesh_extra_tools/mesh_vertex_chamfer.py
index d52cf050c11d3a817d53b156ed1ea555e7af379e..1cc3c7dc43c90a713e7583554278928aea2a2ff5 100644 (file)
@@ -37,6 +37,7 @@ import bmesh
 class VertexChamfer(bpy.types.Operator):
     bl_idname = "mesh.vertex_chamfer"
     bl_label = "Chamfer Vertex"
+    bl_description = "Tri chamfer selected vertices"
     bl_options = {'REGISTER', 'UNDO'}
 
     factor = bpy.props.FloatProperty(name="Factor",
@@ -123,6 +124,22 @@ class VertexChamfer(bpy.types.Operator):
 
         return {'FINISHED'}
 
+class chamfer_help(bpy.types.Operator):
+       bl_idname = 'help.vertexchamfer'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of verts ')
+               layout.label('Result is triangle chamfer, works on single vert.')
+               layout.label('In some cases may need to press F to fill result.')
+       
+       def execute(self, context):
+               return {'FINISHED'}
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
 
 def register():
     bpy.utils.register_module(__name__)
diff --git a/mesh_multi_extrude/__init__.py b/mesh_multi_extrude/__init__.py
deleted file mode 100644 (file)
index 6006ef9..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-#  This program is free software; you can redistribute it and/or
-#  modify it under the terms of the GNU General Public License
-#  as published by the Free Software Foundation; either version 2
-#  of the License, or (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software Foundation,
-#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-# ##### END GPL LICENSE BLOCK #####
-# Contributed to by
-# meta-androcto #
-
-bl_info = {
-    "name": "Multi Extrude Plus",
-    "author": "liero, macouno",
-    "version": (0, 1),
-    "blender": (2, 6, 3),
-    "location": "View3D > Toolbar and View3D > Specials (W-key)",
-    "description": "Add extra curve object types",
-    "warning": "",
-    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
-        "Scripts/Modeling/Multi_Extrude",
-    "tracker_url": "http://projects.blender.org/tracker/index.php?"\
-        "func=detail&aid=28570",
-    "category": "Mesh"}
-
-
-if "bpy" in locals():
-    import imp
-
-else:
-    from . import mesh_bump
-    from . import mesh_mextrude_plus
-
-import bpy
-
-
-class VIEW3D_MT_edit_mesh_bump(bpy.types.Menu):
-    # Define the "Extras" menu
-    bl_idname = "VIEW3D_MT_edit_mesh_bump"
-    bl_label = "Bump"
-
-    def draw(self, context):
-        layout = self.layout
-        layout.operator_context = 'INVOKE_REGION_WIN'
-        layout.operator("mesh.bump",
-            text="Bump")
-
-class ExtrudePanel(bpy.types.Panel):
-    bl_label = 'Multi Extrude Plus'
-    bl_space_type = 'VIEW_3D'
-    bl_region_type = 'TOOLS'
-    bl_options = {'DEFAULT_CLOSED'}
-
-    def draw(self, context):
-        layout = self.layout
-        prop = layout.operator("wm.context_set_value", text="Face Select",
-            icon='FACESEL')
-        prop.value = "(False, False, True)"
-        prop.data_path = "tool_settings.mesh_select_mode"
-        layout.operator('object.mextrude')
-        layout.operator('mesh.bump')
-        layout.operator('object.mesh2bones')
-
-# Register all operators and panels
-
-# Define "Extras" menu
-def menu_func(self, context):
-    self.layout.menu("VIEW3D_MT_edit_mesh_bump", icon="PLUGIN")
-
-
-def register():
-    bpy.utils.register_module(__name__)
-
-    # Add "Extras" menu to the "Add Mesh" menu
-    bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)
-
-
-def unregister():
-    bpy.utils.unregister_module(__name__)
-
-    # Remove "Extras" menu from the "Add Mesh" menu.
-    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
-
-if __name__ == "__main__":
-    register()