Update Mesh Extra Tools: T48120 menu redesign, new features, remove old broken feature
authormeta-androcto <meta.androcto1@gmail.com>
Tue, 19 Apr 2016 03:50:48 +0000 (13:50 +1000)
committermeta-androcto <meta.androcto1@gmail.com>
Tue, 19 Apr 2016 03:50:48 +0000 (13:50 +1000)
17 files changed:
mesh_extra_tools/__init__.py
mesh_extra_tools/face_inset_fillet.py
mesh_extra_tools/mesh_bevel_witold.py [deleted file]
mesh_extra_tools/mesh_bump.py [deleted file]
mesh_extra_tools/mesh_cut_faces.py [new file with mode: 0644]
mesh_extra_tools/mesh_edge_roundifier.py [new file with mode: 0644]
mesh_extra_tools/mesh_edges_length.py [new file with mode: 0644]
mesh_extra_tools/mesh_extras.py [deleted file]
mesh_extra_tools/mesh_fastloop.py [new file with mode: 0644]
mesh_extra_tools/mesh_mextrude_plus.py
mesh_extra_tools/mesh_normal_smooth.py [deleted file]
mesh_extra_tools/mesh_offset_edges.py [new file with mode: 0644]
mesh_extra_tools/mesh_polyredux.py [deleted file]
mesh_extra_tools/mesh_to_wall.py [new file with mode: 0644]
mesh_extra_tools/pkhg_faces.py [new file with mode: 0644]
mesh_extra_tools/random_vertices.py [new file with mode: 0644]
mesh_extra_tools/split_solidify.py [new file with mode: 0644]

index 41de6d9c4e8127f210a927b453b074a2491f57a8..97c6bbd9c79993afd27d3e26c369c2fa9bae39af 100644 (file)
 #
 # ##### END GPL LICENSE BLOCK #####
 # Contributed to by
-# meta-androcto #
+# meta-androcto, pkhg, zmj100, Stanislav Blinov, Piotr Komisarczyk, #
+# Yi Danyang, Giuseppe De Marco, Andy Davies, Gert De Roost, liero, #
+# Hidesato Ikeya, luxuy_BlenderCN, Andrew Hale, Oscurart #
 
 bl_info = {
-    "name": "Extra Tools",
+    "name": "Edit Tools",
     "author": "various",
     "version": (0, 1),
-    "blender": (2, 71, 0),
-    "location": "View3D > Toolshelf > Tools Tab & Specials (W-key)",
+    "blender": (2, 76, 0),
+    "location": "View3D > Toolshelf > Tools & Specials (W-key)",
     "description": "Add extra mesh edit tools",
     "warning": "",
-    "wiki_url": "",
-    "tracker_url": "https://developer.blender.org/maniphest/task/create/?project=3&type=Bug",
+    "wiki_url": "https://github.com/meta-androcto/blenderpython/wiki/AF_Edit_Tools",
+    "tracker_url": "https://developer.blender.org/maniphest/project/3/type/Bug/",
     "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)
+    import importlib
+    importlib.reload(face_inset_fillet)
+    importlib.reload(mesh_filletplus)
+    importlib.reload(mesh_vertex_chamfer)
+    importlib.reload(mesh_mextrude_plus)
+    importlib.reload(mesh_offset_edges)
+    importlib.reload(pkhg_faces)
+    importlib.reload(mesh_edge_roundifier)
+    importlib.reload(mesh_cut_faces)
+    importlib.reload(split_solidify)
+    importlib.reload(mesh_to_wall)
+    importlib.reload(mesh_edges_length)
+    importlib.reload(random_vertices)
+    importlib.reload(mesh_fastloop)
 
 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_offset_edges
+    from . import pkhg_faces
+    from . import mesh_edge_roundifier
+    from . import mesh_cut_faces
+    from . import split_solidify
+    from . import mesh_to_wall
+    from . import mesh_edges_length
+    from . import random_vertices
+    from . import mesh_fastloop
 
-import bpy
+import bpy 
+from bpy.props import BoolProperty
+### ------ MENUS ====== ###
 
 class VIEW3D_MT_edit_mesh_extras(bpy.types.Menu):
     # Define the "Extras" menu
     bl_idname = "VIEW3D_MT_edit_mesh_extras"
-    bl_label = "Extra Tools"
+    bl_label = "Edit Tools"
 
     def draw(self, context):
         layout = self.layout
         layout.operator_context = 'INVOKE_REGION_WIN'
-        layout.operator("faceinfillet.op0_id",
-            text="Face Inset Fillet")
-        layout.operator("fillet.op0_id",
-            text="Edge Fillet Plus")
-        layout.operator("object.mextrude",
-            text="Multi Extrude")
-        layout.operator("mesh.bump",
-            text="Inset Extrude Bump")
-        layout.operator("mesh.mbevel",
-            text="Bevel Selected")
-        layout.operator("mesh.vertex_chamfer",
-            text="Vertex Chamfer")
-        layout.operator("mesh.polyredux",
-            text="Poly Redux")
-        layout.operator("normal.smooth",
-            text="Normal Smooth")
-
-
-class ExtrasPanel(bpy.types.Panel):
-    bl_label = 'Mesh Extra Tools'
+        mode = context.tool_settings.mesh_select_mode
+        if mode[0]:    
+            split = layout.split()             
+            col = split.column()
+            col.label(text="Vert")
+            col.operator("mesh.vertex_chamfer",
+                text="Vertex Chamfer")
+            col.operator("mesh.random_vertices",
+                text="Random Vertices")
+
+            row = split.row(align=True)                
+            col = split.column()
+            col.label(text="Utilities")
+            col.operator("object_ot.fastloop",
+                text="Fast loop")
+            col.operator('mesh.flip_normals', text = 'Normals Flip')
+            col.operator('mesh.remove_doubles', text = 'Remove Doubles')
+            col.operator('mesh.subdivide', text = 'Subdivide')
+            col.operator('mesh.dissolve_limited', text = 'Dissolve Limited')
+
+        elif mode[1]:  
+            split = layout.split()             
+            col = split.column()
+            col.label(text="Edge")
+            col.operator("fillet.op0_id",
+                text="Edge Fillet Plus")
+            col.operator("mesh.offset_edges",
+                text="Offset Edges")
+            col.operator("mesh.edge_roundifier",
+                text="Edge Roundify")
+            col.operator("object.mesh_edge_length_set",
+                text="Set Edge Length")
+            col.operator("bpt.mesh_to_wall",
+                text="Edge(s) to Wall")
+                               
+            row = split.row(align=True)                
+            col = split.column()
+            col.label(text="Utilities")
+            col.operator("object_ot.fastloop",
+                text="Fast loop")
+            col.operator('mesh.flip_normals', text = 'Normals Flip')
+            col.operator('mesh.remove_doubles', text = 'Remove Doubles')
+            col.operator('mesh.remove_doubles', text = 'Remove Doubles')
+            col.operator('mesh.subdivide', text = 'Subdivide')
+            col.operator('mesh.dissolve_limited', text = 'Dissolve Limited')
+
+        elif mode[2]:                  
+            split = layout.split()             
+            col = split.column()
+            col.label(text="Face")
+            col.operator("object.mextrude",
+                text="Multi Extrude")
+            col.operator("faceinfillet.op0_id",
+                text="Face Inset Fillet")
+            col.operator("mesh.add_faces_to_object",
+                text="PKHG Faces")
+            col.operator("mesh.ext_cut_faces",
+                text="Cut Faces")
+            col.operator("sp_sol.op0_id",
+                text="Split Solidify")
+
+            row = split.row(align=True)                
+            col = split.column()
+            col.label(text="Utilities")
+            col.operator("object_ot.fastloop",
+                text="Fast loop")
+            col.operator('mesh.flip_normals', text = 'Normals Flip')
+            col.operator('mesh.remove_doubles', text = 'Remove Doubles')
+            col.operator('mesh.subdivide', text = 'Subdivide')
+            col.operator('mesh.dissolve_limited', text = 'Dissolve Limited')
+
+
+class EditToolsSettings(bpy.types.PropertyGroup):
+
+
+    vert_settings = BoolProperty(
+        name="Vert",
+        default=False)
+
+    edge_settings = BoolProperty(
+        name="Edge",
+        default=False)
+
+    face_settings = BoolProperty(
+        name="Face",
+        default=False)
+
+    utils_settings = BoolProperty(
+        name="Utils",
+        default=False)
+
+
+class EditToolsPanel(bpy.types.Panel):
+    bl_label = 'Mesh Edit Tools'
     bl_space_type = 'VIEW_3D'
     bl_region_type = 'TOOLS'
     bl_context = 'mesh_edit'
@@ -89,52 +178,149 @@ class ExtrasPanel(bpy.types.Panel):
     bl_options = {'DEFAULT_CLOSED'}
 
     def draw(self, context):
+
         layout = self.layout
-        row = layout.split(0.80)
-        row.operator('faceinfillet.op0_id', text = 'Face Inset Fillet', icon = 'PLUGIN')
-        row.operator('help.face_inset', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('fillet.op0_id', text = 'Edge Fillet plus', icon = 'PLUGIN')
-        row.operator('help.edge_fillet', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('object.mextrude', text = 'Multi Face Extrude', icon = 'PLUGIN')
-        row.operator('help.mextrude', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('mesh.bump', text = 'Inset Bump', icon = 'PLUGIN')
-        row.operator('help.bump', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('mesh.mbevel', text = 'Bevel Selected', icon = 'PLUGIN')
-        row.operator('help.edge_bevel', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('mesh.vertex_chamfer', text = 'Vertex Chamfer' , icon = 'PLUGIN')
-        row.operator('help.vertexchamfer', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('mesh.polyredux', text = 'Poly Redux', icon = 'PLUGIN')
-        row.operator('help.polyredux', text = '', icon = 'INFO')
-        row = layout.split(0.80)
-        row.operator('normal.smooth', text = 'Normal Smooth', icon = 'PLUGIN')
-        row.operator('help.normal_smooth', text = '', icon = 'INFO')
-        row = layout.split(0.50)
-        row.operator('mesh.flip_normals', text = 'Normals Flip')
-        row.operator('mesh.remove_doubles', text = 'Remove Doubles')
+        wm = bpy.context.window_manager
+
+        # Vert options
+        box = layout.box()
+        col = box.column(align=False)
+        if wm.edit_tools_settings.vert_settings:
+            file_icon = 'TRIA_DOWN'
+        else:
+            file_icon = 'TRIA_RIGHT'
+        col.prop(wm.edit_tools_settings, "vert_settings",
+                 icon=file_icon, toggle=True)
+        if wm.edit_tools_settings.vert_settings:
+            layout = self.layout
+            row = layout.row()
+            row.label(text="Vert Tools:", icon="VERTEXSEL")
+            row = layout.split(0.70)
+            row.operator('mesh.vertex_chamfer', text = 'Chamfer')
+            row.operator('help.vertexchamfer', text = '?')
+            row = layout.split(0.70)
+            row.operator('mesh.random_vertices', text = 'Random Vertices')
+            row.operator('help.random_vert', text = '?')
+            row = layout.row()
+
+        # Edge options
+        box = layout.box()
+        col = box.column(align=True)
+        if wm.edit_tools_settings.edge_settings:
+            modifier_icon = 'TRIA_DOWN'
+        else:
+            modifier_icon = 'TRIA_RIGHT'
+        col.prop(wm.edit_tools_settings, "edge_settings",
+                 icon=modifier_icon, toggle=True)
+        if wm.edit_tools_settings.edge_settings:
+            layout = self.layout
+            row = layout.row()
+            row.label(text="Edge Tools:", icon="EDGESEL")
+            row = layout.split(0.70)
+            row.operator('fillet.op0_id', text = 'Fillet plus')
+            row.operator('help.edge_fillet', text = '?')
+            row = layout.split(0.70)
+            row.operator('mesh.offset_edges', text = 'Offset Edges')
+            row.operator('help.offset_edges', text = '?')
+            row = layout.split(0.70)
+            row.operator('mesh.edge_roundifier', text = 'Roundify')
+            row.operator('help.roundify', text = '?')
+            row = layout.split(0.70)
+            row.operator('object.mesh_edge_length_set', text = 'Set Edge Length')
+            row.operator('help.roundify', text = '?')
+            row = layout.split(0.70)
+            row.operator('bpt.mesh_to_wall', text = 'Mesh to wall')
+            row.operator('help.wall', text = '?')
+            row = layout.row()
+
+        # Face options
+        box = layout.box()
+        col = box.column(align=True)
+        if wm.edit_tools_settings.face_settings:
+            object_icon = 'TRIA_DOWN'
+        else:
+            object_icon = 'TRIA_RIGHT'
+        col.prop(wm.edit_tools_settings, "face_settings",
+                 icon=object_icon, toggle=True)
+        if wm.edit_tools_settings.face_settings:
+            layout = self.layout
+            row = layout.row()
+            row.label(text="Face Tools:", icon="FACESEL")
+            row = layout.split(0.70)
+            row.operator('object.mextrude', text = 'Multi Extrude')
+            row.operator('help.mextrude', text = '?')
+            row = layout.split(0.70)
+            row.operator('faceinfillet.op0_id', text = 'Inset Fillet')
+            row.operator('help.face_inset', text = '?')
+            row = layout.split(0.70)
+            row.operator('mesh.add_faces_to_object', text = 'Face Extrude')
+            row.operator('help.pkhg', text = '?')
+            row = layout.split(0.70)
+            row.operator('mesh.ext_cut_faces', text = 'Cut Faces')
+            row.operator('help.cut_faces', text = '?')
+            row = layout.split(0.70)
+            row.operator('sp_sol.op0_id', text = 'Split Solidify')
+            row.operator('help.solidify', text = '?')
+            row = layout.row()
+
+        # Utils options
+        box = layout.box()
+        col = box.column(align=True)
+        if wm.edit_tools_settings.utils_settings:
+            rename_icon = 'TRIA_DOWN'
+        else:
+            rename_icon = 'TRIA_RIGHT'
+        col.prop(wm.edit_tools_settings, "utils_settings",
+                 icon=rename_icon, toggle=True)
+        if wm.edit_tools_settings.utils_settings:
+            layout = self.layout
+            row = layout.row()
+            row.label(text="Utilities:")
+            row = layout.row()
+            row = layout.split(0.70)
+            row.operator('object_ot.fastloop', text = 'Fast Loop')
+            row.operator('help.random_vert', text = '?')
+            row = layout.row()
+            row.operator('mesh.flip_normals', text = 'Normals Flip')
+            row = layout.row()
+            row.operator('mesh.remove_doubles', text = 'Remove Doubles')
+            row = layout.row()
+            row.operator('mesh.subdivide', text = 'Subdivide')
+            row = layout.row()
+            row.operator('mesh.dissolve_limited', text = 'Dissolve Limited')
+# Addons Preferences
+class AddonPreferences(bpy.types.AddonPreferences):
+       bl_idname = __name__
+       
+       def draw(self, context):
+               layout = self.layout
+               layout.label(text="----Mesh Edit Tools----")
+               layout.label(text="Collection of extra Mesh Edit Functions")
+               layout.label(text="Edit Mode toolshelf or W key specials")
 
 # 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__)
+    wm = bpy.context.window_manager
+    bpy.types.WindowManager.edit_tools_settings = bpy.props.PointerProperty(type=EditToolsSettings)
 
     # Add "Extras" menu to the "Add Mesh" menu
-    bpy.types.VIEW3D_MT_edit_mesh_specials.prepend(menu_func)
+    bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
 
 
 def unregister():
+
+    wm = bpy.context.window_manager
     bpy.utils.unregister_module(__name__)
 
     # Remove "Extras" menu from the "Add Mesh" menu.
     bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
 
+    del bpy.types.WindowManager.edit_tools_settings
+
 if __name__ == "__main__":
     register()
index 2d0d542cb33512c1433003baf7aad30e28a33882..e83470873e5070879b6a9543405987f48547e236 100644 (file)
@@ -80,12 +80,12 @@ def face_inset_fillet(bme, face_index_list, inset_amount, distance, number_of_si
                     val=((f.normal).normalized() * inset_amount)
                 else:
                     val=-((f.normal).normalized() * inset_amount)
-                p6 = angle_rotation(p,p + val,vec1,radians(90))
+                p6 = angle_rotation(p,p + val,vec1,radians(90))                
             else:
                 #if the corner is an actual corner
                 val=((f.normal).normalized() * h)
                 if out==True:
-                    # -(p - (vec2.normalized() * adj))) is the axis
+                    #this shit -(p - (vec2.normalized() * adj))) is just the freaking axis afaik...
                     p6 = angle_rotation(p,p + val, -(p - (vec2.normalized() * adj)),-radians(90))
                 else:
                     p6 = angle_rotation(p,p - val,((p - (vec1.normalized() * adj)) - (p - (vec2.normalized() * adj))),
@@ -121,19 +121,23 @@ def face_inset_fillet(bme, face_index_list, inset_amount, distance, number_of_si
                     d = distance / tan(ang_ * 0.5)
                 #max(d) is vec1_.magnitude*0.5
                 #or vec2_.magnitude*0.5 respectively
-
+                
+                #only functional difference v
+                
                 if d >vec1_.magnitude*0.5:
                     d=vec1_.magnitude*0.5
                 if d >vec2_.magnitude*0.5:
                     d=vec2_.magnitude*0.5
-
+                    
+                #only functional difference ^ 
+                    
                 q3 = q - (vec1_.normalized() * d)
                 q4 = q - (vec2_.normalized() * d)
                 #these are new verts somewhat offset from the coners
                 rp_ = q - ((q - ((q3 + q4) * 0.5)).normalized() * h_)
                 #reference point inside the curvature
                 axis_ = vec1_.cross(vec2_)
-                #face normal
+                #this should really be just the face normal
                 vec3_ = rp_ - q3
                 vec4_ = rp_ - q4
                 rot_ang = vec3_.angle(vec4_)
@@ -141,7 +145,7 @@ def face_inset_fillet(bme, face_index_list, inset_amount, distance, number_of_si
                 
                 for o in range(number_of_sides + 1):
                     
-                    #calculates the actual new vertices
+                    #this calculates the actual new vertices
                     
                     q5 = angle_rotation(rp_,q4,axis_,rot_ang * o / number_of_sides)
                     v = bme.verts.new(q5)
@@ -227,7 +231,7 @@ class faceinfillet_op0(bpy.types.Operator):
         row2.prop(self, 'distance')
 
     def execute(self, context):
-        #prepares everything for the main function
+        #this really just prepares everything for the main function
         inset_amount = self.inset_amount
         number_of_sides = self.number_of_sides
         distance = self.distance
diff --git a/mesh_extra_tools/mesh_bevel_witold.py b/mesh_extra_tools/mesh_bevel_witold.py
deleted file mode 100644 (file)
index 8b4bf13..0000000
+++ /dev/null
@@ -1,197 +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 #####
-'''
-Bevel add-on
-'''
-#--- ### Header
-bl_info = {
-    "name": "Bevel witold",
-    "author": "Witold Jaworski",
-    "version": (1, 2, 0),
-    "blender": (2, 57, 0),
-    "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('or Select 2 or more verices & bevel.')
-               layout.label('To Help:')
-               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 = 350)
-#--- ### 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 = "Bevel Selected"
-    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!")
diff --git a/mesh_extra_tools/mesh_bump.py b/mesh_extra_tools/mesh_bump.py
deleted file mode 100644 (file)
index f07fda2..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-# mesh_bump.py Copyright (C) 2011, Dolf Veenvliet
-#
-# Extrude a selection from a mesh multiple times
-#
-# ***** 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": "Bump",
-    "author": "Dolf Veenvliet",
-    "version": 1,
-    "blender": (2, 56, 0),
-    "location": "View3D > Specials > Bump",
-    "description": "Extrude and translate/rotate/scale multiple times",
-    "warning": "",
-    "wiki_url": "",
-    "tracker_url": "",
-    "category": "Mesh"}
-
-"""
-Usage:
-
-Launch from "W-menu"
-
-Additional links:
-       Author Site: http://www.macouno.com
-       e-mail: dolf {at} macouno {dot} com
-"""
-
-import bpy
-from bpy.props import EnumProperty, FloatVectorProperty, FloatProperty, BoolProperty
-from . import mesh_extras
-
-# Bump stuff!
-class Bump():
-
-       # Initialise the class
-       def __init__(self, context, type, scale, steps):
-
-               self.ob = context.active_object
-               bpy.ops.object.mode_set(mode='OBJECT')
-
-               translate = mesh_extras.get_average_outer_edge_length()
-               #inset = mesh_extras.get_shortest_outer_edge_length() * 0.25
-
-               translate *= scale
-
-               bpy.ops.object.mode_set(mode='EDIT')
-
-               stepped = 0
-
-               # Simple... we just do a bunch of steps... set in stone... easy!
-               if type == 'BUM':
-
-                       self.extrude()
-
-                       bpy.ops.object.mode_set(mode='OBJECT')
-                       bpy.ops.object.mode_set(mode='EDIT')
-
-                       self.shrink(-translate)
-                       self.shrink(translate*0.3)
-
-                       stepped += 1
-
-               # Spike!
-               elif type == 'SPI':
-
-                       for i in range(3):
-
-                               self.extrude()
-
-                               bpy.ops.object.mode_set(mode='OBJECT')
-                               bpy.ops.object.mode_set(mode='EDIT')
-
-                               if not i:
-                                       f = 0.5
-                               elif i == 1:
-                                       f = 0.3
-                               elif i == 2:
-                                       f = 0.2
-
-                               t = translate * f
-
-                               self.shrink(-t)
-                               self.shrink(t * (2 * f))
-
-                               stepped += 1
-
-               # Dimple!
-               elif type == 'DIM' or type == 'PIM':
-
-                       self.extrude()
-                       bpy.ops.object.mode_set(mode='OBJECT')
-                       bpy.ops.object.mode_set(mode='EDIT')
-
-                       self.shrink(-translate * 0.2)
-
-                       self.extrude()
-                       bpy.ops.object.mode_set(mode='OBJECT')
-                       bpy.ops.object.mode_set(mode='EDIT')
-
-                       self.shrink(translate * 0.2)
-                       self.shrink(-translate * 0.2)
-
-                       if type == 'PIM':
-                               self.extrude()
-                               bpy.ops.object.mode_set(mode='OBJECT')
-                               bpy.ops.object.mode_set(mode='EDIT')
-
-                               self.shrink(-translate * 0.2)
-                               stepped = 3
-                       else:
-                               stepped = 2
-
-
-
-
-               if steps:
-                       self.ob['growsteps'] = stepped
-
-
-
-       # Extrude the selection (do not move it)
-       def extrude(self):
-               bpy.ops.mesh.extrude_faces_move()
-
-       # SHrink!
-       def shrink(self,v):
-               bpy.ops.transform.shrink_fatten(value=v, mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
-
-
-
-class Bump_init(bpy.types.Operator):
-       '''Bump by extruding and moving/rotating/scaling multiple times'''
-       bl_idname = 'mesh.bump'
-       bl_label = 'Inset Extrude Bump'
-       bl_options = {'REGISTER', 'UNDO'}
-
-       # The falloffs we use
-       types=(
-               ('BUM', 'Bump',''),
-               ('SPI', 'Spike',''),
-               ('DIM', 'Dimple',''),
-               ('PIM', 'Pimple',''),
-               )
-
-       type = EnumProperty(items=types, name='Type', description='The type of bump', default='BUM')
-
-       # Scale
-       scale = FloatProperty(name='Scale factor', description='Translation in Blender units', default=1.0, min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, step=10, precision=2)
-
-       steps = BoolProperty(name='Retain steps', description='Keep the step count in a property', default=False)
-
-       @classmethod
-       def poll(cls, context):
-               obj = context.active_object
-               return (obj and obj.type == 'MESH' and bpy.context.tool_settings.mesh_select_mode[0] == False and bpy.context.tool_settings.mesh_select_mode[1] == False and bpy.context.tool_settings.mesh_select_mode[2] == True)
-
-       def execute(self, context):
-               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('To Help:')
-               layout.label('Keep extrusions small to prevent overlapping')
-               layout.label('Do not select all faces')
-               layout.label('if using with create armature, enter object mode first')
-
-       def execute(self, context):
-               return {'FINISHED'}
-
-       def invoke(self, context, event):
-               return context.window_manager.invoke_popup(self, width = 350)
-'''
-def menu_func(self, context):
-       self.layout.operator(Bump_init.bl_idname, text="Bump")
-
-def register():
-       bpy.utils.register_module(__name__)
-       bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
-
-def unregister():
-       bpy.utils.unregister_module(__name__)
-       bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
-
-if __name__ == "__main__":
-       register()
-'''
\ No newline at end of file
diff --git a/mesh_extra_tools/mesh_cut_faces.py b/mesh_extra_tools/mesh_cut_faces.py
new file mode 100644 (file)
index 0000000..2f5c9a0
--- /dev/null
@@ -0,0 +1,254 @@
+bl_info = {
+    "name" : "Cut Faces",
+    "author" : "Stanislav Blinov",
+    "version" : (1, 0, 0),
+    "blender" : (2, 72, 0),
+    "description" : "Cut Faces and Deselect Boundary operators",
+    "category" : "Mesh",}
+
+import bpy
+import bmesh
+
+def bmesh_from_object(object):
+    mesh = object.data
+    if object.mode == 'EDIT':
+        bm = bmesh.from_edit_mesh(mesh)
+    else:
+        bm = bmesh.new()
+        bm.from_mesh(mesh)
+    return bm
+
+def bmesh_release(bm, object):
+    mesh = object.data
+    bm.select_flush_mode()
+    if object.mode == 'EDIT':
+        bmesh.update_edit_mesh(mesh, True)
+    else:
+        bm.to_mesh(mesh)
+        bm.free()
+
+def calc_face(face, keep_caps=True):
+
+    assert face.tag
+
+    def radial_loops(loop):
+        next = loop.link_loop_radial_next
+        while next != loop:
+            result, next = next, next.link_loop_radial_next
+            yield result
+
+    result = []
+
+    face.tag = False
+    selected = []
+    to_select = []
+    for loop in face.loops:
+        self_selected = False
+        # Iterate over selected adjacent faces
+        for radial_loop in filter(lambda l: l.face.select, radial_loops(loop)):
+            # Tag the edge if no other face done so already
+            if not loop.edge.tag:
+                loop.edge.tag = True
+                self_selected = True
+
+            adjacent_face = radial_loop.face
+            # Only walk adjacent face if current face tagged the edge
+            if adjacent_face.tag and self_selected:
+                result += calc_face(adjacent_face, keep_caps)
+
+        if loop.edge.tag:
+            (selected, to_select)[self_selected].append(loop)
+
+    for loop in to_select:
+        result.append(loop.edge)
+        selected.append(loop)
+
+    # Select opposite edge in quads
+    if keep_caps and len(selected) == 1 and len(face.verts) == 4:
+        result.append(selected[0].link_loop_next.link_loop_next.edge)
+
+    return result
+
+def get_edge_rings(bm, keep_caps=True):
+
+    def tag_face(face):
+        if face.select:
+            face.tag = True
+            for edge in face.edges: edge.tag = False
+        return face.select
+
+    # fetch selected faces while setting up tags
+    selected_faces = [ f for f in bm.faces if tag_face(f) ]
+
+    edges = []
+
+    try:
+        # generate a list of edges to select:
+        # traversing only tagged faces, since calc_face can walk and untag islands
+        for face in filter(lambda f: f.tag, selected_faces): edges += calc_face(face, keep_caps)
+    finally:
+        # housekeeping: clear tags
+        for face in selected_faces:
+            face.tag = False
+            for edge in face.edges: edge.tag = False
+
+    return edges
+
+class MESH_xOT_deselect_boundary(bpy.types.Operator):
+    """Deselect boundary edges of selected faces"""
+    bl_idname = "mesh.ext_deselect_boundary"
+    bl_label = "Deselect Boundary"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    keep_cap_edges = bpy.props.BoolProperty(
+        name        = "Keep Cap Edges",
+        description = "Keep quad strip cap edges selected",
+        default     = False)
+
+    @classmethod
+    def poll(cls, context):
+        active_object = context.active_object
+        return active_object and active_object.type == 'MESH' and active_object.mode == 'EDIT'
+
+    def execute(self, context):
+        object = context.active_object
+        bm = bmesh_from_object(object)
+
+        try:
+            edges = get_edge_rings(bm, keep_caps = self.keep_cap_edges)
+            if not edges:
+                self.report({'WARNING'}, "No suitable selection found")
+                return {'CANCELLED'}
+
+            bpy.ops.mesh.select_all(action='DESELECT')
+            bm.select_mode = {'EDGE'}
+
+            for edge in edges:
+                edge.select = True
+            context.tool_settings.mesh_select_mode[:] = False, True, False
+
+        finally:
+            bmesh_release(bm, object)
+
+        return {'FINISHED'}
+
+class cut_faces_help(bpy.types.Operator):
+       bl_idname = 'help.cut_faces'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of Faces.')
+               layout.label('Some Functions work on plane only')
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+
+class MESH_xOT_cut_faces(bpy.types.Operator):
+    """Cut selected faces, connecting through their adjacent edges"""
+    bl_idname = "mesh.ext_cut_faces"
+    bl_label = "Cut Faces"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    # from bmesh_operators.h
+    SUBD_INNERVERT    = 0
+    SUBD_PATH         = 1
+    SUBD_FAN          = 2
+    SUBD_STRAIGHT_CUT = 3
+
+    num_cuts = bpy.props.IntProperty(
+        name    = "Number of Cuts",
+        default = 1,
+        min     = 1,
+        max     = 100,
+        subtype = 'UNSIGNED')
+
+    use_single_edge = bpy.props.BoolProperty(
+        name        = "Quad/Tri Mode",
+        description = "Cut boundary faces",
+        default     = False)
+
+    corner_type = bpy.props.EnumProperty(
+        items = [('SUBD_INNERVERT', "Inner Vert", ""),
+                 ('SUBD_PATH', "Path", ""),
+                 ('SUBD_FAN', "Fan", ""),
+                 ('SUBD_STRAIGHT_CUT', "Straight Cut", ""),],
+        name = "Quad Corner Type",
+        description = "How to subdivide quad corners",
+        default = 'SUBD_STRAIGHT_CUT')
+
+    use_grid_fill = bpy.props.BoolProperty(
+        name        = "Use Grid Fill",
+        description = "Fill fully enclosed faces with a grid",
+        default     = True)
+
+    @classmethod
+    def poll(cls, context):
+        active_object = context.active_object
+        return active_object and active_object.type == 'MESH' and active_object.mode == 'EDIT'
+
+    def cut_edges(self, context):
+        object = context.active_object
+        bm = bmesh_from_object(object)
+
+        try:
+            edges = get_edge_rings(bm, keep_caps = True)
+            if not edges:
+                self.report({'WARNING'}, "No suitable selection found")
+                return False
+
+            result = bmesh.ops.subdivide_edges(
+                bm,
+                edges = edges,
+                cuts = int(self.num_cuts),
+                use_grid_fill = bool(self.use_grid_fill),
+                use_single_edge = bool(self.use_single_edge),
+                quad_corner_type = eval("self."+self.corner_type))
+
+            bpy.ops.mesh.select_all(action='DESELECT')
+            bm.select_mode = {'EDGE'}
+
+            inner = result['geom_inner']
+            for edge in filter(lambda e: isinstance(e, bmesh.types.BMEdge), inner):
+                edge.select = True
+
+        finally:
+            bmesh_release(bm, object)
+
+        return True
+
+    def execute(self, context):
+
+        if not self.cut_edges(context):
+            return {'CANCELLED'}
+
+        context.tool_settings.mesh_select_mode[:] = False, True, False
+        # Try to select all possible loops
+        bpy.ops.mesh.loop_multi_select(ring=False)
+        return {'FINISHED'}
+
+def menu_deselect_boundary(self, context):
+    self.layout.operator(MESH_xOT_deselect_boundary.bl_idname)
+
+def menu_cut_faces(self, context):
+    self.layout.operator(MESH_xOT_cut_faces.bl_idname)
+
+def register():
+    bpy.utils.register_class(MESH_xOT_deselect_boundary)
+    bpy.utils.register_class(MESH_xOT_cut_faces)
+
+    if __name__ != "__main__":
+        bpy.types.VIEW3D_MT_select_edit_mesh.append(menu_deselect_boundary)
+        bpy.types.VIEW3D_MT_edit_mesh_faces.append(menu_cut_faces)
+
+def unregister():
+    bpy.utils.unregister_class(MESH_xOT_deselect_boundary)
+    bpy.utils.unregister_class(MESH_xOT_cut_faces)
+
+    if __name__ != "__main__":
+        bpy.types.VIEW3D_MT_select_edit_mesh.remove(menu_deselect_boundary)
+        bpy.types.VIEW3D_MT_edit_mesh_faces.remove(menu_cut_faces)
+
+if __name__ == "__main__":
+    register()
diff --git a/mesh_extra_tools/mesh_edge_roundifier.py b/mesh_extra_tools/mesh_edge_roundifier.py
new file mode 100644 (file)
index 0000000..0b17316
--- /dev/null
@@ -0,0 +1,1234 @@
+# ***** 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": "Edge Roundifier",
+    "category": "Mesh",
+    'author': 'Piotr Komisarczyk (komi3D), PKHG',
+    'version': (1, 0, 0),
+    'blender': (2, 7, 3),
+    'location': 'SPACE > Edge Roundifier or CTRL-E > Edge Roundifier or Tools > Addons > Edge Roundifier',
+    'description': 'Mesh editing script allowing edge rounding',
+    'wiki_url': '',
+    'tracker_url': '',
+    'category': 'Mesh'
+}
+
+import bmesh
+import bpy
+import bpy.props
+import imp
+from math import sqrt, acos, asin, pi, radians, degrees, sin, acos
+from mathutils import Vector, Euler, Matrix, Quaternion
+import types
+
+
+# CONSTANTS
+two_pi = 2 * pi #PKHG>??? maybe other constantly used values too???
+XY = "XY"
+XZ = "XZ"
+YZ = "YZ"
+SPIN_END_THRESHOLD = 0.001
+LINE_TOLERANCE = 0.0001
+
+
+# variable controlling all print functions
+#PKHG>??? to be replaced, see debugPrintNew ;-)
+debug = True
+
+def debugPrint(*text):
+    if debug:
+        for t in text:
+            print(text)
+
+
+
+############# for debugging PKHG ################
+def debugPrintNew(debug,*text):
+    if debug:
+        tmp = [el for el in text]
+        for row in tmp:
+            print(row)
+
+d_XABS_YABS = False
+d_Edge_Info = False
+d_Plane = False
+d_Radius_Angle = False
+d_Roots = False
+d_RefObject = False
+d_LineAB = False
+d_Selected_edges = False
+d_Rotate_Around_Spin_Center = False
+###################################################################################
+
+
+####################### Geometry and math calcualtion methods #####################
+
+class CalculationHelper:
+    def __init__(self):
+        '''
+        Constructor
+        '''
+    def getLineCoefficientsPerpendicularToVectorInPoint(self, point, vector, plane):
+        x, y, z = point
+        xVector, yVector, zVector = vector
+        destinationPoint = (x + yVector, y - xVector, z)
+        if plane == 'YZ':
+            destinationPoint = (x , y + zVector, z - yVector)
+        if plane == 'XZ':
+            destinationPoint = (x + zVector, y, z - xVector)
+        return self.getCoefficientsForLineThrough2Points(point, destinationPoint, plane)
+
+    def getQuadraticRoots(self, coef):
+        if len(coef) != 3:
+            return NaN
+        else:
+            a, b, c = coef
+            delta = b ** 2 - 4 * a * c
+            if delta == 0:
+                x = -b / (2 * a)
+                return (x, x)
+            elif delta < 0:
+                return None
+            else :
+                x1 = (-b - sqrt(delta)) / (2 * a)
+                x2 = (-b + sqrt(delta)) / (2 * a)
+                return (x1, x2)
+
+    def getCoefficientsForLineThrough2Points(self, point1, point2, plane):
+        x1, y1, z1 = point1
+        x2, y2, z2 = point2
+
+        # mapping x1,x2, y1,y2 to proper values based on plane
+        if plane == YZ:
+            x1 = y1
+            x2 = y2
+            y1 = z1
+            y2 = z2
+        if plane == XZ:
+            y1 = z1
+            y2 = z2
+
+        # Further calculations the same as for XY plane
+        xabs = abs(x2 - x1)
+        yabs = abs(y2 - y1) 
+        debugPrintNew(d_XABS_YABS, "XABS = " + str( xabs)+ " YABS = " + str(yabs))
+
+        if xabs <= LINE_TOLERANCE:
+            return None  # this means line x = edgeCenterX
+        if yabs <= LINE_TOLERANCE:
+            A = 0
+            B = y1
+            return A, B
+        A = (y2 - y1) / (x2 - x1)
+        B = y1 - (A * x1)
+        return (A, B)
+
+    def getLineCircleIntersections(self, lineAB, circleMidPoint, radius):
+        # (x - a)**2 + (y - b)**2 = r**2 - circle equation
+        # y = A*x + B - line equation
+        # f * x**2 + g * x + h = 0 - quadratic equation
+        A, B = lineAB
+        a, b = circleMidPoint
+        f = 1 + (A ** 2)
+        g = -2 * a + 2 * A * B - 2 * A * b
+        h = (B ** 2) - 2 * b * B - (radius ** 2) + (a ** 2) + (b ** 2)
+        coef = [f, g, h]
+        roots = self.getQuadraticRoots(coef)
+        if roots != None:
+            x1 = roots[0]
+            x2 = roots[1]
+            point1 = [x1, A * x1 + B]
+            point2 = [x2, A * x2 + B]
+            return [point1, point2]
+        else:
+            return None
+
+    def getLineCircleIntersectionsWhenXPerpendicular(self, edgeCenter, circleMidPoint, radius, plane):
+        # (x - a)**2 + (y - b)**2 = r**2 - circle equation
+        # x = xValue - line equation
+        # f * x**2 + g * x + h = 0 - quadratic equation
+        xValue = edgeCenter[0]
+        if plane == YZ:
+            xValue = edgeCenter[1]
+        if plane == XZ:
+            xValue = edgeCenter[0]
+
+        a, b = circleMidPoint
+        f = 1
+        g = -2 * b
+        h = (a ** 2) + (b ** 2) + (xValue ** 2) - 2 * a * xValue - (radius ** 2)
+        coef = [f, g, h]
+        roots = self.getQuadraticRoots(coef)
+        if roots != None:
+            y1 = roots[0]
+            y2 = roots[1]
+            point1 = [xValue, y1]
+            point2 = [xValue, y2]
+            return [point1, point2]
+        else:
+            return None
+
+    # point1 is the point near 90 deg angle
+    def getAngle(self, point1, point2, point3):
+        distance1 = (Vector(point1) - Vector(point2)).length
+        distance2 = (Vector(point2) - Vector(point3)).length
+        cos = distance1 / distance2
+        
+        if abs(cos) > 1:  # prevents Domain Error
+            cos = round(cos)
+        
+        alpha = acos(cos)
+        return (alpha, degrees(alpha))
+
+    # get two of three coordinates used for further calculation of spin center
+    #PKHG>nice if rescriction to these 3 types or planes is to be done
+    #komi3D> from 0.0.2 there is a restriction. In future I would like Edge Roundifier to work on
+    #komi3D> Normal and View coordinate systems. That would be great... 
+    def getCircleMidPointOnPlane(self, V1, plane):
+        X = V1[0]
+        Y = V1[1]
+        if plane == 'XZ':
+            X = V1[0]
+            Y = V1[2]
+        elif plane == 'YZ':
+            X = V1[1]
+            Y = V1[2]
+        return [X, Y]
+        
+
+    def getEdgeReference(self, edge, edgeCenter, plane):
+        vert1 = edge.verts[1].co
+        V = vert1 - edgeCenter
+        orthoVector = Vector((V[1], -V[0], V[2]))
+        if plane == 'XZ':
+            orthoVector = Vector((V[2], V[1], -V[0]))
+        elif plane == 'YZ':
+            orthoVector = Vector((V[0], V[2], -V[1]))
+        refPoint = edgeCenter + orthoVector
+        return refPoint
+        
+        
+########################################################
+################# SELECTION METHODS ####################
+
+class SelectionHelper:
+    def selectVertexInMesh(self, mesh, vertex):
+        bpy.ops.object.mode_set(mode = "OBJECT")
+        for v in mesh.vertices:
+            if v.co == vertex:
+                v.select = True
+                break
+
+        bpy.ops.object.mode_set(mode = "EDIT")
+
+    def getSelectedVertex(self, mesh):
+        bpy.ops.object.mode_set(mode = "OBJECT")
+        for v in mesh.vertices:
+            if v.select == True :
+                bpy.ops.object.mode_set(mode = "EDIT")
+                return v
+
+        bpy.ops.object.mode_set(mode = "EDIT")
+        return None
+
+    def refreshMesh(self, bm, mesh):
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bm.to_mesh(mesh)
+        bpy.ops.object.mode_set(mode = 'EDIT')
+
+class roundify_help(bpy.types.Operator):
+    bl_idname = 'help.roundify'
+    bl_label = ''
+
+    def draw(self, context):
+        layout = self.layout
+        layout.label('To use:')
+        layout.label('Select a face or faces & inset.')
+        layout.label('Inset square, circle or outside.')
+        layout.label('To Help:')
+        layout.label('Circle: use remove doubles to tidy joins.')
+        layout.label('Outset: select & use normals flip before extruding.')
+    
+    def execute(self, context):
+        return {'FINISHED'}
+
+    def invoke(self, context, event):
+        return context.window_manager.invoke_popup(self, width = 350)
+
+###################################################################################
+
+class EdgeRoundifier(bpy.types.Operator):
+    """Edge Roundifier"""  # blender will use this as a tooltip for menu items and buttons.
+    bl_idname = "mesh.edge_roundifier"  # unique identifier for buttons and menu items to reference.
+    bl_label = "Edge Roundifier"  # display name in the interface.
+    bl_options = {'REGISTER', 'UNDO', 'PRESET'}  # enable undo for the operator.PKHG>INFO and PRESET
+
+    threshold = 0.0005
+    
+    obj = None
+
+    edgeScaleFactor = bpy.props.FloatProperty(name = '', default = 1.0, min = 0.00001, max = 100000.0, step = 0.5, precision = 5)
+    r = bpy.props.FloatProperty(name = '', default = 1, min = 0.00001, max = 1000.0, step = 0.1, precision = 3)
+    a = bpy.props.FloatProperty(name = '', default = 180.0, min = 0.1, max = 180.0, step = 0.5, precision = 1)
+    n = bpy.props.IntProperty(name = '', default = 4, min = 1, max = 100, step = 1)
+    flip = bpy.props.BoolProperty(name = 'Flip', default = False)
+    
+    invertAngle = bpy.props.BoolProperty(name = 'Invert', default = False)
+    fullCircles = bpy.props.BoolProperty(name = 'Circles', default = False)
+    bothSides = bpy.props.BoolProperty(name = 'Both sides', default = False)
+    
+    drawArcCenters = bpy.props.BoolProperty(name = 'Centers', default = False)
+    removeEdges = bpy.props.BoolProperty(name = 'Edges', default = False)
+    removeScaledEdges = bpy.props.BoolProperty(name = 'Scaled edges', default = False)
+    
+    connectArcWithEdge = bpy.props.BoolProperty(name = 'Arc - Edge', default = False)
+    connectArcs = bpy.props.BoolProperty(name = 'Arcs', default = False)
+    connectScaledAndBase = bpy.props.BoolProperty(name = 'Scaled - Base Edge', default = False)
+    connectArcsFlip = bpy.props.BoolProperty(name = 'Flip Arcs', default = False)
+    connectArcWithEdgeFlip = bpy.props.BoolProperty(name = 'Flip Arc - Edge', default = False)
+    
+    
+    axisAngle = bpy.props.FloatProperty(name = '', default = 0.0, min = -180.0, max = 180.0, step = 0.5, precision = 1)
+    edgeAngle = bpy.props.FloatProperty(name = '', default = 0.0, min = -180.0, max = 180.0, step = 0.5, precision = 1)
+    offset = bpy.props.FloatProperty(name = '', default = 0.0, min = -1000000.0, max = 1000000.0, step = 0.1, precision = 5)
+    offset2 = bpy.props.FloatProperty(name = '', default = 0.0, min = -1000000.0, max = 1000000.0, step = 0.1, precision = 5)
+    ellipticFactor = bpy.props.FloatProperty(name = '', default = 0.0, min = -1000000.0, max = 1000000.0, step = 0.1, precision = 5)
+    
+    
+    workModeItems = [("Normal", "Normal", ""), ("Reset", "Reset", "")]
+    workMode = bpy.props.EnumProperty(
+        items = workModeItems,
+        name = '',
+        default = 'Normal',
+        description = "Edge Roundifier work mode")
+    
+    entryModeItems = [("Radius", "Radius", ""), ("Angle", "Angle", "")]
+    entryMode = bpy.props.EnumProperty(
+        items = entryModeItems,
+        name = '',
+        default = 'Angle',
+        description = "Edge Roundifier entry mode")
+    
+    rotateCenterItems = [("Spin", "Spin", ""), ("V1", "V1", ""), ("Edge", "Edge", ""), ("V2", "V2", "")]
+    rotateCenter = bpy.props.EnumProperty(
+        items = rotateCenterItems,
+        name = '',
+        default = 'Edge',
+        description = "Rotate center for spin axis rotate")
+    
+    arcModeItems = [("FullEdgeArc","Full","Full"),('HalfEdgeArc',"Half","Half")]
+    arcMode = bpy.props.EnumProperty(
+        items = arcModeItems,
+        name = '',
+        default = 'FullEdgeArc',
+        description = "Edge Roundifier arc mode")
+    
+
+    angleItems = [('Other', "Other", "User defined angle"), ('180', "180", "HemiCircle"), ('120', "120", "TriangleCircle"),
+                    ('90', "90", "QuadCircle"), ('72', "72", "PentagonCircle"), ('60', "60", "HexagonCircle"), 
+                    ('45', "45", "OctagonCircle"), ('30', "30", "12-gonCircle")]
+
+    angleEnum = bpy.props.EnumProperty(
+        items = angleItems,
+        name = '',
+        default = '180',
+        description = "Presets prepare standard angles and calculate proper ray")
+
+    refItems = [('ORG', "Origin", "Use Origin Location"), ('CUR', "3D Cursor", "Use 3DCursor Location")
+                , ('EDG', "Edge", "Use Individual Edge Reference")]
+    referenceLocation = bpy.props.EnumProperty(
+        items = refItems,
+        name = '',
+        default = 'ORG',
+        description = "Reference location used by Edge Roundifier to calculate initial centers of drawn arcs")
+
+    planeItems = [(XY, XY, "XY Plane (Z=0)"), (YZ, YZ, "YZ Plane (X=0)"), (XZ, XZ, "XZ Plane (Y=0)")]
+    planeEnum = bpy.props.EnumProperty(
+        items = planeItems,
+        name = '',
+        default = 'XY',
+        description = "Plane used by Edge Roundifier to calculate spin plane of drawn arcs")
+
+    edgeScaleCenterItems = [('V1', "V1", "v1"), ('CENTER', "Center", "cent"), ('V2', "V2", "v2")]
+    edgeScaleCenterEnum = bpy.props.EnumProperty(
+        items = edgeScaleCenterItems,
+        name = 'edge scale center',
+        default = 'CENTER',
+        description = "Center used for scaling initial edge")
+
+    calc = CalculationHelper()
+    sel = SelectionHelper()
+    
+    def prepareMesh(self, context):
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bpy.ops.object.mode_set(mode = 'EDIT')
+
+        mesh = context.scene.objects.active.data
+        bm = bmesh.new()
+        bm.from_mesh(mesh)
+        
+        edges = [ele for ele in bm.edges if ele.select]
+        return edges, mesh, bm
+
+    def prepareParameters(self):
+        parameters = { "a" : "a"}
+        parameters["arcMode"] = self.arcMode
+        parameters["edgeScaleFactor"] = self.edgeScaleFactor
+        parameters["edgeScaleCenterEnum"] = self.edgeScaleCenterEnum
+        parameters["plane"] = self.planeEnum
+        parameters["radius"] = self.r
+        parameters["angle"] = self.a
+        parameters["segments"] = self.n
+        parameters["fullCircles"] = self.fullCircles
+        parameters["invertAngle"] = self.invertAngle
+        parameters["bothSides"] = self.bothSides
+        parameters["angleEnum"] = self.angleEnum
+        parameters["entryMode"] = self.entryMode
+        parameters["workMode"] = self.workMode
+        parameters["refObject"] = self.referenceLocation
+        parameters["flip"] = self.flip
+        parameters["drawArcCenters"] = self.drawArcCenters
+        parameters["removeEdges"] = self.removeEdges
+        parameters["removeScaledEdges"] = self.removeScaledEdges
+        parameters["connectArcWithEdge"] = self.connectArcWithEdge
+        parameters["connectScaledAndBase"] = self.connectScaledAndBase
+        parameters["connectArcs"] = self.connectArcs
+        parameters["connectArcsFlip"] = self.connectArcsFlip
+        parameters["connectArcWithEdgeFlip"] = self.connectArcWithEdgeFlip
+        parameters["axisAngle"] = self.axisAngle
+        parameters["edgeAngle"] = self.edgeAngle
+        parameters["offset"] = self.offset
+        parameters["offset2"] = self.offset2
+        parameters["ellipticFactor"] = self.ellipticFactor
+        parameters["rotateCenter"] = self.rotateCenter
+        return parameters
+
+    def draw(self, context):
+        layout = self.layout
+        box = layout.box()
+        uiPercentage = 0.333
+
+        self.addEnumParameterToUI(box, False, uiPercentage, 'Mode:', 'workMode')
+        self.addEnumParameterToUI(box, False, uiPercentage, 'Plane:', 'planeEnum')
+        self.addEnumParameterToUI(box, False, uiPercentage, 'Reference:', 'referenceLocation')
+
+        box = layout.box()
+        self.addEnumParameterToUI(box, False, uiPercentage, 'Scale base:', 'edgeScaleCenterEnum')
+        self.addParameterToUI(box, False, uiPercentage, 'Scale factor:', 'edgeScaleFactor')
+
+        box = layout.box()
+        self.addEnumParameterToUI(box, False, uiPercentage, 'Entry mode:', 'entryMode')
+        
+        row = box.row (align = False)
+        row.prop(self, 'angleEnum', expand = True, text = "angle presets")
+
+        self.addParameterToUI(box, False, uiPercentage, 'Angle:', 'a')
+        self.addParameterToUI(box, False, uiPercentage, 'Radius:', 'r')
+        self.addParameterToUI(box, False, uiPercentage, 'Segments:', 'n')
+
+###################################
+        box = layout.box()
+        self.addTwoCheckboxesToUI(box, False, 'Options:', 'flip','invertAngle')
+        self.addTwoCheckboxesToUI(box, False, '', 'bothSides','fullCircles')
+        self.addCheckboxToUI(box, False, '', 'drawArcCenters')
+
+        box = layout.box()
+        self.addTwoCheckboxesToUI(box, False, 'Remove:', 'removeEdges','removeScaledEdges')
+
+        box = layout.box()
+        self.addTwoCheckboxesToUI(box, False, 'Connect:', 'connectArcs','connectArcsFlip')
+        self.addTwoCheckboxesToUI(box, False, '', 'connectArcWithEdge','connectArcWithEdgeFlip')
+        self.addCheckboxToUI(box, False, '', 'connectScaledAndBase')
+################################
+                
+        box = layout.box()
+        self.addParameterToUI(box, False, uiPercentage, 'Orhto offset:', 'offset')
+        self.addParameterToUI(box, False, uiPercentage, 'Parallel offset:', 'offset2')
+        
+        box = layout.box()
+        self.addParameterToUI(box, False, uiPercentage, 'Edge rotate :', 'edgeAngle')
+        self.addEnumParameterToUI(box, False, uiPercentage, 'Axis rotate center:', 'rotateCenter')
+        self.addParameterToUI(box, False, uiPercentage, 'Axis rotate:', 'axisAngle')
+        
+        box = layout.box()
+        self.addParameterToUI(box, False, uiPercentage, 'Elliptic factor:', 'ellipticFactor')
+
+    def addParameterToUI(self, layout, alignment, percent, label, property):
+        row = layout.row (align = alignment)
+        split = row.split(percentage=percent)
+        col = split.column()
+        col.label(label)
+        col2 = split.column()
+        row = col2.row(align = alignment)
+        row.prop(self, property)
+        
+    def addTwoCheckboxesToUI(self, layout, alignment, label, property1, property2):
+        row = layout.row (align = alignment)
+        row.label(label)
+        row.prop(self, property1)
+        row.prop(self, property2)
+        
+    def addCheckboxToUI(self, layout, alignment, label, property1):
+        row = layout.row (align = alignment)
+        row.label(label)
+        row.prop(self, property1)
+        row.label('')
+        
+    def addEnumParameterToUI(self, layout, alignment, percent, label, property):
+        row = layout.row (align = alignment)
+        split = row.split(percentage=percent)
+        col = split.column()
+        col.label(label)
+        col2 = split.column()
+        row = col2.row(align = alignment)
+        row.prop(self, property, expand = True, text = "a")
+
+    def execute(self, context):
+            
+        edges, mesh, bm = self.prepareMesh(context)
+        parameters = self.prepareParameters()
+        
+        self.resetValues(parameters["workMode"])
+        
+        self.obj = context.scene.objects.active
+        scaledEdges = self.scaleDuplicatedEdges(bm, edges, parameters)
+
+        if len(scaledEdges) > 0:
+            self.roundifyEdges(scaledEdges, parameters, bm, mesh)
+            
+            if parameters["connectScaledAndBase"]:
+                self.connectScaledEdgesWithBaseEdge(scaledEdges, edges, bm, mesh)
+            
+            self.sel.refreshMesh(bm, mesh)
+            self.selectEdgesAfterRoundifier(context, scaledEdges)
+        else:
+            debugPrint("No edges selected!")
+        
+        if parameters["removeEdges"]:
+            bmesh.ops.delete(bm, geom = edges, context = 2)
+        if parameters["removeScaledEdges"] and self.edgeScaleFactor != 1.0:    
+            bmesh.ops.delete(bm, geom = scaledEdges, context = 2)
+            
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bm.to_mesh(mesh)
+        bpy.ops.object.mode_set(mode = 'EDIT')
+
+        bm.free()
+        return {'FINISHED'}
+
+##########################################
+    def resetValues(self, workMode):
+        if workMode == "Reset":
+            self.setAllParamsToDefaults()
+        
+    def setAllParamsToDefaults(self):
+        self.edgeScaleFactor = 1.0
+        self.r = 1
+        self.a = 180.0
+        self.n = 4
+        self.flip = False
+        self.invertAngle = False
+        self.fullCircles = False
+        self.bothSides = False
+        self.drawArcCenters = False
+        self.removeEdges = False
+        self.removeScaledEdges = False
+        
+        self.connectArcWithEdge = False
+        self.connectArcs = False
+        self.connectScaledAndBase = False
+        self.connectArcsFlip = False
+        self.connectArcWithEdgeFlip = False
+        
+        
+        self.axisAngle = 0.0
+        self.edgeAngle = 0.0
+        self.offset = 0.0
+        self.offset2 = 0.0
+        self.ellipticFactor = 0.0
+        
+        self.workMode = 'Normal'
+        self.entryMode = 'Angle'
+        self.angleEnum = '180'
+        self.referenceLocation = 'ORG'
+        self.planeEnum = 'XY'
+        self.edgeScaleCenterEnum = 'CENTER'
+        self.rotateCenter = 'Edge'
+        ######
+        
+    def scaleDuplicatedEdges(self,bm, edges, parameters):
+        scaleCenter = parameters["edgeScaleCenterEnum"]
+        factor = parameters["edgeScaleFactor"]
+        #this code is based on Zeffi's answer to my question
+        duplicateEdges=[]
+        if factor == 1:
+            duplicateEdges = edges
+        else:
+            for e in edges:
+                v1 = e.verts[0].co
+                v2 = e.verts[1].co
+                origin = None
+                if scaleCenter == 'CENTER':
+                    origin = (v1+v2) * 0.5  
+                elif scaleCenter == 'V1':
+                    origin = v1  
+                elif scaleCenter == 'V2':
+                    origin = v2  
+                    
+                bmv1 = bm.verts.new(((v1-origin) * factor) + origin)
+                bmv2 = bm.verts.new(((v2-origin) * factor) + origin)
+                bme = bm.edges.new([bmv1, bmv2])
+                duplicateEdges.append(bme)
+        return duplicateEdges
+        
+        
+    def roundifyEdges(self, edges, parameters, bm, mesh):
+        arcs = []
+        for e in edges:
+            arcVerts = self.roundify(e, parameters, bm, mesh)
+            arcs.append(arcVerts)
+
+        if parameters["connectArcs"]: 
+            self.connectArcsTogether(arcs, bm, mesh, parameters)
+
+    def getNormalizedEdgeVector (self, edge):
+        V1 = edge.verts[0].co 
+        V2 = edge.verts[1].co 
+        edgeVector =  V2 - V1
+        normEdge = edgeVector.normalized()
+        return normEdge
+
+    def getEdgePerpendicularVector (self,edge, plane):
+        normEdge = self.getNormalizedEdgeVector(edge)
+
+        edgePerpendicularVector = Vector((normEdge[1], -normEdge[0], 0))
+        if plane == YZ:
+            edgePerpendicularVector = Vector((0, normEdge[2], -normEdge[1]))
+        if plane == XZ:
+            edgePerpendicularVector = Vector((normEdge[2], 0, -normEdge[0]))
+        return edgePerpendicularVector
+            
+    def getEdgeInfo(self, edge):
+        V1 = edge.verts[0].co 
+        V2 = edge.verts[1].co 
+        edgeVector =  V2 - V1 
+        edgeLength = edgeVector.length 
+        edgeCenter = (V2 + V1) * 0.5 
+        return V1, V2, edgeVector, edgeLength, edgeCenter
+
+
+    def roundify(self, edge, parameters, bm, mesh):
+        V1, V2, edgeVector, edgeLength, edgeCenter = self.getEdgeInfo(edge)
+        if self.skipThisEdge(V1, V2, parameters["plane"]):
+            return
+
+        roundifyParams = None
+        arcVerts = None
+        roundifyParams = self.calculateRoundifyParams(edge, parameters, bm, mesh)
+        if roundifyParams == None:
+            return
+            
+        arcVerts = self.spinAndPostprocess(edge, parameters, bm, mesh, edgeCenter, roundifyParams)
+        return arcVerts
+                 
+    def spinAndPostprocess(self, edge, parameters, bm, mesh, edgeCenter, roundifyParams):
+        spinnedVerts,roundifyParamsUpdated = self.drawSpin(edge, edgeCenter, roundifyParams, parameters, bm, mesh)
+        postProcessedArcVerts = self.arcPostprocessing(edge, parameters, bm, mesh, roundifyParamsUpdated, spinnedVerts, edgeCenter)
+        return postProcessedArcVerts
+
+    def rotateArcAroundEdge(self, bm, mesh, arcVerts, parameters):
+        angle = parameters["edgeAngle"]
+        if angle != 0:
+            self.arc_rotator(arcVerts, angle, parameters)
+
+    # arc_rotator method was created by PKHG, I (komi3D) adjusted it to fit the rest of this addon    
+    def arc_rotator(self, arcVerts, extra_rotation, parameters):
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        old_location = self.obj.location.copy()
+        bpy.ops.transform.translate(value = - old_location, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
+        bpy.ops.object.mode_set(mode = 'EDIT')
+        adjust_matrix = self.obj.matrix_parent_inverse
+        bm = bmesh.from_edit_mesh(self.obj.data)
+        lastVert = len(arcVerts) - 1
+        if parameters["drawArcCenters"]:
+            lastVert = lastVert - 1 #center gets added as last vert of arc
+        v0_old = adjust_matrix  *  arcVerts[0].co.copy()
+        v1_old = adjust_matrix * arcVerts[lastVert].co.copy()
+
+        #PKHG>INFO move if necessary v0 to origin such that the axis gos through origin and v1
+        if v0_old != Vector((0,0,0)):
+            for i, ele in enumerate(arcVerts):
+                arcVerts[i].co += - v0_old   
+
+        axis =  arcVerts[0].co - arcVerts[lastVert].co 
+   
+        a_quat = Quaternion(axis, radians(extra_rotation)).normalized()
+        a_mat = Quaternion(axis, radians(extra_rotation)).normalized().to_matrix()
+    
+        for ele in arcVerts:
+            ele.co = a_mat * ele.co
+    
+        #PKHG>INFO move back if needed
+        if v0_old != Vector((0,0,0)):
+            for i, ele in enumerate(arcVerts):
+                arcVerts[i].co += + v0_old   
+    
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        #PKHG>INFO move origin object back print("old location = " , old_location)
+        bpy.ops.transform.translate(value = old_location, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
+        bpy.ops.object.mode_set(mode = 'EDIT')
+    
+    def makeElliptic(self, bm, mesh, arcVertices, parameters):
+        if parameters["ellipticFactor"] != 0: #if 0 then nothing has to be done
+            lastVert = len(arcVertices) - 1
+            if parameters["drawArcCenters"]:
+                lastVert = lastVert - 1 #center gets added as last vert of arc
+            v0co = arcVertices[0].co
+            v1co = arcVertices[lastVert].co
+            
+            for vertex in arcVertices: #range(len(res_list)):
+                #PKHg>INFO compute the base on the edge  of the height-vector
+                top = vertex.co #res_list[nr].co
+                t = 0
+                if v1co - v0co != 0 :
+                    t = (v1co - v0co).dot(top - v0co)/(v1co - v0co).length ** 2 
+                h_bottom = v0co + t * (v1co - v0co)
+                height = (h_bottom - top )
+                vertex.co = top + parameters["ellipticFactor"] * height
+        
+        return arcVertices
+        
+    def arcPostprocessing(self, edge, parameters, bm, mesh, roundifyParams, spinnedVerts, edgeCenter):
+        [chosenSpinCenter, otherSpinCenter, spinAxis, angle, steps, refObjectLocation] = roundifyParams
+        rotatedVerts = []
+        if parameters["rotateCenter"] == 'Edge':
+            rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, edgeCenter)
+        elif parameters["rotateCenter"] == 'Spin':
+            rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, chosenSpinCenter)
+        elif parameters["rotateCenter"] == 'V1':
+            rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, edge.verts[0].co)
+        elif parameters["rotateCenter"] == 'V2':
+            rotatedVerts = self.rotateArcAroundSpinAxis(bm, mesh, spinnedVerts, parameters, edge.verts[1].co)
+        
+        offsetVerts = self.offsetArcPerpendicular(bm, mesh, rotatedVerts, edge, parameters)
+        offsetVerts2 = self.offsetArcParallel(bm, mesh, offsetVerts, edge, parameters)
+        ellipticVerts = self.makeElliptic(bm,mesh,offsetVerts2,parameters)
+        self.rotateArcAroundEdge(bm, mesh, ellipticVerts, parameters)
+
+        if parameters["connectArcWithEdge"]:
+            self.connectArcTogetherWithEdge(edge,offsetVerts2,bm,mesh, parameters)
+        return offsetVerts2
+        
+    def connectArcTogetherWithEdge(self, edge, arcVertices, bm, mesh, parameters):
+        lastVert = len(arcVertices) - 1
+        if parameters["drawArcCenters"]:
+            lastVert = lastVert - 1 #center gets added as last vert of arc    
+        edgeV1 = edge.verts[0].co
+        edgeV2 = edge.verts[1].co
+        arcV1 = arcVertices[0].co
+        arcV2 = arcVertices[lastVert].co
+        
+        bmv1 = bm.verts.new(edgeV1)
+        bmv2 = bm.verts.new(arcV1)
+        
+        bmv3 = bm.verts.new(edgeV2)
+        bmv4 = bm.verts.new(arcV2)
+        
+        if parameters["connectArcWithEdgeFlip"] == False: 
+            bme = bm.edges.new([bmv1, bmv2])
+            bme2 = bm.edges.new([bmv3, bmv4])
+        else:
+            bme = bm.edges.new([bmv1, bmv4])
+            bme2 = bm.edges.new([bmv3, bmv2])
+        self.sel.refreshMesh(bm, mesh)
+        
+    def connectScaledEdgesWithBaseEdge(self, scaledEdges, baseEdges, bm, mesh):
+        for i in range(0, len(scaledEdges)):
+            scaledEdgeV1 = scaledEdges[i].verts[0].co
+            baseEdgeV1 = baseEdges[i].verts[0].co
+            scaledEdgeV2 = scaledEdges[i].verts[1].co
+            baseEdgeV2 = baseEdges[i].verts[1].co
+            
+            bmv1 = bm.verts.new(baseEdgeV1)
+            bmv2 = bm.verts.new(scaledEdgeV1)
+            bme = bm.edges.new([bmv1, bmv2])
+            
+            bmv3 = bm.verts.new(scaledEdgeV2)
+            bmv4 = bm.verts.new(baseEdgeV2)
+            bme = bm.edges.new([bmv3, bmv4])
+        self.sel.refreshMesh(bm, mesh)
+            
+    def connectArcsTogether(self, arcs, bm, mesh, parameters):
+        for i in range(0, len(arcs)-1):
+            if arcs[i] == None or arcs[i + 1] == None: # in case on XZ or YZ there are no arcs drawn
+                return
+            lastVert = len(arcs[i])-1
+            if parameters["drawArcCenters"]:
+                lastVert = lastVert - 1 #center gets added as last vert of arc    
+            #take last vert of arc i and first vert of arc i+1
+            
+            V1 = arcs[i][lastVert].co
+            V2 = arcs[i+1][0].co
+            
+            if parameters["connectArcsFlip"]:
+                V1 = arcs[i][0].co
+                V2 = arcs[i+1][lastVert].co
+            
+            bmv1 = bm.verts.new(V1)
+            bmv2 = bm.verts.new(V2)
+            bme = bm.edges.new([bmv1, bmv2])
+        
+        #connect last arc and first one
+        lastArcId = len(arcs)-1
+        lastVertIdOfLastArc = len(arcs[lastArcId])-1
+        if parameters["drawArcCenters"]:
+                lastVertIdOfLastArc = lastVertIdOfLastArc - 1 #center gets added as last vert of arc   
+        V1 = arcs[lastArcId][lastVertIdOfLastArc].co
+        V2 = arcs[0][0].co
+        if parameters["connectArcsFlip"]:
+                V1 = arcs[lastArcId][0].co
+                V2 = arcs[0][lastVertIdOfLastArc].co
+
+        bmv1 = bm.verts.new(V1)
+        bmv2 = bm.verts.new(V2)
+        bme = bm.edges.new([bmv1, bmv2])
+                    
+        self.sel.refreshMesh(bm, mesh)
+
+    def offsetArcPerpendicular(self, bm, mesh, Verts, edge, parameters):
+        perpendicularVector = self.getEdgePerpendicularVector(edge, parameters["plane"])
+        offset = parameters["offset"]
+        translation = offset * perpendicularVector
+        
+        try:
+            bmesh.ops.translate(
+            bm,
+            verts=Verts,
+            vec=translation)
+        except ValueError:
+            print ("[Edge Roundifier]: Perpendicular translate value error - multiple vertices in list - try unchecking 'Centers'")
+        
+        indexes = [v.index for v in Verts] 
+        self.sel.refreshMesh(bm, mesh)
+        offsetVertices = [bm.verts[i] for i in indexes]
+        return offsetVertices
+    
+    def offsetArcParallel(self, bm, mesh, Verts, edge, parameters):
+        edgeVector = self.getNormalizedEdgeVector(edge)
+        offset = parameters["offset2"]
+        translation = offset * edgeVector
+        
+        try:
+            bmesh.ops.translate(
+            bm,
+            verts=Verts,
+            vec=translation)
+        except ValueError:
+            print ("[Edge Roundifier]: Parallel translate value error - multiple vertices in list - try unchecking 'Centers'")
+        
+        indexes = [v.index for v in Verts] 
+        self.sel.refreshMesh(bm, mesh)
+        offsetVertices = [bm.verts[i] for i in indexes]
+        return offsetVertices
+        
+        
+    def skipThisEdge(self, V1, V2, plane):
+        # Check If It is possible to spin selected verts on this plane if not exit roundifier
+        if(plane == XY):
+            if (V1[0] == V2[0] and V1[1] == V2[1]):
+                return True
+        elif(plane == YZ):
+            if (V1[1] == V2[1] and V1[2] == V2[2]):
+                return True
+        elif(plane == XZ):
+            if (V1[0] == V2[0] and V1[2] == V2[2]):
+                return True
+        return False
+
+    def calculateRoundifyParams(self, edge, parameters, bm, mesh):
+        # BECAUSE ALL DATA FROM MESH IS IN LOCAL COORDINATES
+        # AND SPIN OPERATOR WORKS ON GLOBAL COORDINATES
+        # WE FIRST NEED TO TRANSLATE ALL INPUT DATA BY VECTOR EQUAL TO ORIGIN POSITION AND THEN PERFORM CALCULATIONS
+        # At least that is my understanding :) <komi3D>
+
+        # V1 V2 stores Local Coordinates
+        V1, V2, edgeVector, edgeLength, edgeCenter = self.getEdgeInfo(edge)
+
+        debugPrintNew(d_Plane, "PLANE: " +  parameters["plane"])
+        lineAB = self.calc.getLineCoefficientsPerpendicularToVectorInPoint(edgeCenter, edgeVector, parameters["plane"])
+        circleMidPoint = V1
+        circleMidPointOnPlane = self.calc.getCircleMidPointOnPlane(V1, parameters["plane"])
+        radius = parameters["radius"]
+
+        angle = 0
+        if (parameters["entryMode"] == 'Angle'):
+            if (parameters["angleEnum"] != 'Other'):
+                radius, angle = self.CalculateRadiusAndAngleForAnglePresets(parameters["angleEnum"], radius, angle, edgeLength)
+            else:
+                radius, angle = self.CalculateRadiusAndAngle(edgeLength)
+        debugPrintNew(d_Radius_Angle, "RADIUS = " + str(radius) + "  ANGLE = " + str( angle))
+        roots = None
+        if angle != pi:  # mode other than 180
+            if lineAB == None:
+                roots = self.calc.getLineCircleIntersectionsWhenXPerpendicular(edgeCenter, circleMidPointOnPlane, radius, parameters["plane"])
+            else:
+                roots = self.calc.getLineCircleIntersections(lineAB, circleMidPointOnPlane, radius)
+            if roots == None:
+                debugPrint("[Edge Roundifier]: No centers were found. Change radius to higher value")
+                return None
+            roots = self.addMissingCoordinate(roots, V1, parameters["plane"])  # adds X, Y or Z coordinate
+        else:
+            roots = [edgeCenter, edgeCenter]
+        debugPrintNew(d_Roots, "roots=" + str(roots))
+
+        refObjectLocation = None
+        objectLocation = bpy.context.active_object.location  # Origin Location
+
+        if parameters["refObject"] == "ORG":
+            refObjectLocation = [0, 0, 0]
+        elif parameters["refObject"] == "CUR":
+            refObjectLocation = bpy.context.scene.cursor_location - objectLocation
+        else:
+            refObjectLocation = self.calc.getEdgeReference(edge, edgeCenter, parameters["plane"])
+
+        debugPrintNew(d_RefObject, parameters["refObject"], refObjectLocation)
+        chosenSpinCenter, otherSpinCenter = self.getSpinCenterClosestToRefCenter(refObjectLocation, roots)
+
+        if (parameters["entryMode"] == "Radius"):
+            halfAngle = self.calc.getAngle(edgeCenter, chosenSpinCenter, circleMidPoint)
+            angle = 2 * halfAngle[0]  # in radians
+            self.a = degrees(angle)  # in degrees
+
+        spinAxis = self.getSpinAxis(parameters["plane"])
+        steps = parameters["segments"]
+        angle = -angle #rotate clockwise by default
+        
+        return [chosenSpinCenter, otherSpinCenter, spinAxis, angle, steps, refObjectLocation]
+    
+    def drawSpin(self, edge, edgeCenter, roundifyParams, parameters, bm, mesh):
+        [chosenSpinCenter, otherSpinCenter, spinAxis, angle, steps, refObjectLocation] = roundifyParams
+
+        v0org, v1org = (edge.verts[0], edge.verts[1])
+
+        if parameters["flip"]:
+            angle = -angle
+            spinCenterTemp = chosenSpinCenter
+            chosenSpinCenter = otherSpinCenter
+            otherSpinCenter = spinCenterTemp
+            
+        if(parameters["invertAngle"]):
+            if angle < 0:
+                angle = two_pi + angle
+            elif angle > 0:
+                angle = -two_pi + angle
+            else:
+                angle = two_pi
+
+        if(parameters["fullCircles"]):
+            angle = two_pi
+
+        v0 = bm.verts.new(v0org.co)
+        
+        result = bmesh.ops.spin(bm, geom = [v0], cent = chosenSpinCenter, axis = spinAxis, \
+                                   angle = angle, steps = steps, use_duplicate = False)
+
+        # it seems there is something wrong with last index of this spin...
+        # I need to calculate the last index manually here...
+        vertsLength = len(bm.verts)
+        bm.verts.ensure_lookup_table()
+        lastVertIndex = bm.verts[vertsLength - 1].index
+        lastSpinVertIndices = self.getLastSpinVertIndices(steps, lastVertIndex)
+
+        self.sel.refreshMesh(bm, mesh)
+        
+        alternativeLastSpinVertIndices = []
+        bothSpinVertices = []
+        spinVertices = []
+        alternate = False
+        
+        if ((angle == pi or angle == -pi) and not parameters["bothSides"]):
+
+            midVertexIndex = lastVertIndex - round(steps / 2)
+            bm.verts.ensure_lookup_table()
+            midVert = bm.verts[midVertexIndex].co
+
+            midVertexDistance = (Vector(refObjectLocation) - Vector(midVert)).length 
+            midEdgeDistance = (Vector(refObjectLocation) - Vector(edgeCenter)).length
+
+
+            if ((parameters["invertAngle"]) or (parameters["flip"] )) :
+                if (midVertexDistance > midEdgeDistance):
+                    alternativeLastSpinVertIndices = self.alternateSpin(bm, mesh, angle, chosenSpinCenter, spinAxis, steps, v0, v1org, lastSpinVertIndices)
+                                    
+            else:
+                if (midVertexDistance < midEdgeDistance):
+                    alternativeLastSpinVertIndices = self.alternateSpin(bm, mesh, angle, chosenSpinCenter, spinAxis, steps, v0, v1org, lastSpinVertIndices)
+
+        elif (angle != two_pi):  # to allow full circles :)
+            if (result['geom_last'][0].co - v1org.co).length > SPIN_END_THRESHOLD:
+                alternativeLastSpinVertIndices = self.alternateSpin(bm, mesh, angle, chosenSpinCenter, spinAxis, steps, v0, v1org, lastSpinVertIndices)
+                alternate = True
+
+        self.sel.refreshMesh(bm, mesh)
+        if alternativeLastSpinVertIndices != []:
+            lastSpinVertIndices = alternativeLastSpinVertIndices
+        
+       
+        if lastSpinVertIndices.stop <= len(bm.verts): #make sure arc was added to bmesh
+            spinVertices = [ bm.verts[i] for i in lastSpinVertIndices]
+            if alternativeLastSpinVertIndices != []:
+                spinVertices = spinVertices + [v0] 
+            else:
+                spinVertices = [v0] + spinVertices
+            
+        if (parameters["bothSides"]):
+                #do some more testing here!!!
+            if (angle == pi or angle == -pi):
+                alternativeLastSpinVertIndices = self.alternateSpinNoDelete(bm, mesh, -angle, chosenSpinCenter, spinAxis, steps, v0, v1org, [])
+            elif alternate:
+                alternativeLastSpinVertIndices = self.alternateSpinNoDelete(bm, mesh, angle, otherSpinCenter, spinAxis, steps, v0, v1org, [])
+            elif not alternate:    
+                alternativeLastSpinVertIndices = self.alternateSpinNoDelete(bm, mesh, -angle, otherSpinCenter, spinAxis, steps, v0, v1org, [])
+            bothSpinVertices = [ bm.verts[i] for i in lastSpinVertIndices]
+            alternativeSpinVertices= [ bm.verts[i] for i in alternativeLastSpinVertIndices]
+            bothSpinVertices = [v0] + bothSpinVertices + alternativeSpinVertices 
+            spinVertices = bothSpinVertices
+            
+        if (parameters["fullCircles"]):
+            v1 = bm.verts.new(v1org.co)
+            spinVertices = spinVertices + [v1]
+            
+        if (parameters['drawArcCenters']):
+            centerVert = bm.verts.new(chosenSpinCenter)
+            spinVertices.append(centerVert)
+            
+        return spinVertices,[chosenSpinCenter, otherSpinCenter, spinAxis, angle, steps, refObjectLocation]
+
+##########################################
+
+    def deleteSpinVertices(self, bm, mesh, lastSpinVertIndices):
+        verticesForDeletion = []
+        bm.verts.ensure_lookup_table()
+        for i in lastSpinVertIndices:
+            vi = bm.verts[i]
+            vi.select = True
+            debugPrint(str(i) + ") " + str(vi))
+            verticesForDeletion.append(vi)
+
+        bmesh.ops.delete(bm, geom = verticesForDeletion, context = 1)
+        bmesh.update_edit_mesh(mesh, True)
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bpy.ops.object.mode_set(mode = 'EDIT')
+
+    def alternateSpinNoDelete(self, bm, mesh, angle, chosenSpinCenter, spinAxis, steps, v0, v1org, lastSpinVertIndices):
+        v0prim = v0
+       
+        result2 = bmesh.ops.spin(bm, geom = [v0prim], cent = chosenSpinCenter, axis = spinAxis,
+            angle = angle, steps = steps, use_duplicate = False)
+        vertsLength = len(bm.verts)
+        bm.verts.ensure_lookup_table()
+        lastVertIndex2 = bm.verts[vertsLength - 1].index
+
+        lastSpinVertIndices2 = self.getLastSpinVertIndices(steps, lastVertIndex2)
+        return lastSpinVertIndices2
+
+    def alternateSpin(self, bm, mesh, angle, chosenSpinCenter, spinAxis, steps, v0, v1org, lastSpinVertIndices):
+        self.deleteSpinVertices(bm, mesh, lastSpinVertIndices)
+        v0prim = v0
+       
+        result2 = bmesh.ops.spin(bm, geom = [v0prim], cent = chosenSpinCenter, axis = spinAxis,
+            angle = -angle, steps = steps, use_duplicate = False)
+        # it seems there is something wrong with last index of this spin...
+        # I need to calculate the last index manually here...
+        vertsLength = len(bm.verts)
+        bm.verts.ensure_lookup_table()
+        lastVertIndex2 = bm.verts[vertsLength - 1].index
+
+        lastSpinVertIndices2 = self.getLastSpinVertIndices(steps, lastVertIndex2)
+        # second spin also does not hit the v1org
+        if (result2['geom_last'][0].co - v1org.co).length > SPIN_END_THRESHOLD:
+            
+            self.deleteSpinVertices(bm, mesh, lastSpinVertIndices2)
+            self.deleteSpinVertices(bm, mesh, range(v0.index, v0.index + 1))
+            return []
+        else:
+            return lastSpinVertIndices2
+
+    def getLastSpinVertIndices(self, steps, lastVertIndex):
+        arcfirstVertexIndex = lastVertIndex - steps + 1
+        lastSpinVertIndices = range(arcfirstVertexIndex, lastVertIndex + 1)
+        return lastSpinVertIndices
+
+
+##################        
+        
+    def rotateArcAroundSpinAxis(self, bm, mesh, vertices, parameters, edgeCenter):
+        
+        axisAngle = parameters["axisAngle"]
+        plane = parameters["plane"]
+        #compensate rotation center
+        objectLocation = bpy.context.active_object.location
+        #center = objectLocation + chosenSpinCenter 
+        center = objectLocation + edgeCenter
+        
+        rot = Euler( (0.0, 0.0, radians(axisAngle)),'XYZ' ).to_matrix()
+        if plane == YZ:
+            rot = Euler( (radians(axisAngle),0.0, 0.0 ),'XYZ' ).to_matrix()
+        if plane == XZ:
+            rot = Euler( (0.0, radians(axisAngle),0.0),'XYZ' ).to_matrix()
+           
+        indexes = [v.index for v in vertices] 
+
+        bmesh.ops.rotate(
+                    bm,
+                    cent=center,
+                    matrix=rot,
+                    verts=vertices,
+                    space=bpy.context.edit_object.matrix_world
+                    )
+        self.sel.refreshMesh(bm, mesh)
+        bm.verts.ensure_lookup_table()
+        rotatedVertices = [bm.verts[i] for i in indexes]
+        
+        return rotatedVertices
+            
+    def deleteSpinVertices(self, bm, mesh, lastSpinVertIndices):
+        verticesForDeletion = []
+        bm.verts.ensure_lookup_table()
+        for i in lastSpinVertIndices:
+            vi = bm.verts[i]
+            vi.select = True
+            verticesForDeletion.append(vi)
+
+        bmesh.ops.delete(bm, geom = verticesForDeletion, context = 1)
+        bmesh.update_edit_mesh(mesh, True)
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bpy.ops.object.mode_set(mode = 'EDIT')
+
+    def getLastSpinVertIndices(self, steps, lastVertIndex):
+        arcfirstVertexIndex = lastVertIndex - steps + 1
+        lastSpinVertIndices = range(arcfirstVertexIndex, lastVertIndex + 1)
+        return lastSpinVertIndices
+
+    def CalculateRadiusAndAngle(self, edgeLength):
+        degAngle = self.a
+        angle = radians(degAngle)
+        self.r = radius = edgeLength / (2 * sin(angle / 2))
+        return radius, angle
+    
+    def CalculateRadiusAndAngleForAnglePresets(self, angleEnum, initR, initA, edgeLength):
+        radius = initR
+        angle = initA
+
+        if angleEnum == "180":
+            self.a = 180
+        elif angleEnum == "120":
+            self.a = 120
+        elif angleEnum == "90":
+            self.a = 90
+        elif angleEnum == "72":
+            self.a = 72
+        elif angleEnum == "60":
+            self.a = 60
+        elif angleEnum == "45":
+            self.a = 45
+        elif angleEnum == "30":
+            self.a = 30
+        return self.CalculateRadiusAndAngle(edgeLength)
+                      
+    def getSpinCenterClosestToRefCenter(self, objLocation, roots):
+        root0Distance = (Vector(objLocation) - Vector(roots[0])).length
+        root1Distance = (Vector(objLocation) - Vector(roots[1])).length 
+
+        chosenId = 0
+        rejectedId = 1
+        if (root0Distance > root1Distance):
+            chosenId = 1
+            rejectedId = 0
+        return roots[chosenId], roots[rejectedId]
+
+    def addMissingCoordinate(self, roots, startVertex, plane):
+        if roots != None:
+            a, b = roots[0]
+            c, d = roots[1]
+            if plane == XY:
+                roots[0] = Vector((a, b, startVertex[2]))
+                roots[1] = Vector((c, d, startVertex[2]))
+            if plane == YZ:
+                roots[0] = Vector((startVertex[0], a, b))
+                roots[1] = Vector((startVertex[0], c, d))
+            if plane == XZ:
+                roots[0] = Vector((a, startVertex[1], b))
+                roots[1] = Vector((c, startVertex[1], d))
+        return roots
+
+    def selectEdgesAfterRoundifier(self, context, edges):
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bpy.ops.object.mode_set(mode = 'EDIT')
+        mesh = context.scene.objects.active.data
+        bmnew = bmesh.new()
+        bmnew.from_mesh(mesh)
+        self.deselectEdges(bmnew)
+        for selectedEdge in edges:
+            for e in bmnew.edges:
+                if (e.verts[0].co - selectedEdge.verts[0].co).length <= self.threshold \
+                   and (e.verts[1].co - selectedEdge.verts[1].co).length <= self.threshold:
+                    e.select_set(True)
+
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bmnew.to_mesh(mesh)
+        bmnew.free()
+        bpy.ops.object.mode_set(mode = 'EDIT')
+
+
+    def deselectEdges(self, bm):
+        for edge in bm.edges:
+            edge.select_set(False)
+
+    def getSpinAxis(self, plane):
+        axis = (0, 0, 1)
+        if plane == YZ:
+            axis = (1, 0, 0)
+        if plane == XZ:
+            axis = (0, 1, 0)
+        return axis
+
+    @classmethod
+    def poll(cls, context):
+        return (context.scene.objects.active.type == 'MESH') and (context.scene.objects.active.mode == 'EDIT')
+    
+def draw_item(self, context):
+    self.layout.operator_context = 'INVOKE_DEFAULT'
+    self.layout.operator('mesh.edge_roundifier')
+
+
+def register():
+    bpy.utils.register_class(EdgeRoundifier)
+    bpy.utils.register_class(EdgeWorksPanel)
+    bpy.types.VIEW3D_MT_edit_mesh_edges.append(draw_item)
+
+
+def unregister():
+    bpy.utils.unregister_class(EdgeRoundifier)
+    bpy.utils.unregister_class(EdgeWorksPanel)
+    bpy.types.VIEW3D_MT_edit_mesh_edges.remove(draw_item)
+
+if __name__ == "__main__":
+    register()
diff --git a/mesh_extra_tools/mesh_edges_length.py b/mesh_extra_tools/mesh_edges_length.py
new file mode 100644 (file)
index 0000000..16c2a3b
--- /dev/null
@@ -0,0 +1,279 @@
+bl_info = {
+    'name': "set edges length",
+    'description': "edges length",
+    'author': "Giuseppe De Marco [BlenderLab] inspired by NirenYang",
+    'version': (0, 1, 0),
+    'blender': (2, 7, 0, 5),
+    'location': '[Toolbar][Tools][Mesh Tools]: set Length(Shit+Alt+E)',
+    'warning': "",
+    'category': 'Mesh',
+    "wiki_url": "",
+    "tracker_url": "",
+}
+
+
+import bpy
+import bmesh
+import mathutils
+from bpy.props import BoolProperty, FloatProperty, EnumProperty, StringProperty
+
+edge_length_debug = False
+_error_message = 'Please select one or more edge to fill select_history'
+
+
+def get_edge_vector( edge ):
+    verts = ( edge.verts[0].co, edge.verts[1].co)
+    
+    #if verts[1] >= verts[0]:
+        #vector = verts[1] - verts[0]
+    #else:
+        #vector = verts[0] - verts[1]
+    vector = verts[1] - verts[0]
+    return vector
+
+def get_selected(bmesh_obj, geometry_type):
+    """
+    geometry type should be edges, verts or faces 
+    """
+    selected = []
+    for i in getattr(bmesh_obj, geometry_type):
+        if i.select:
+            selected.append(i)
+    return tuple(selected)
+
+def get_center_vector( verts ):
+    """
+    verts = [mathutils.Vector((x,y,z)), mathutils.Vector((x,y,z))]
+    """
+    center_vector = mathutils.Vector( ((( verts[1][0] + verts[0][0] )/2.)
+                                    , (( verts[1][1] + verts[0][1] )/2.)
+                                    , (( verts[1][2] + verts[0][2] )/2.) ) )
+    return center_vector
+    
+
+class LengthSet(bpy.types.Operator):
+    bl_idname = "object.mesh_edge_length_set"
+    bl_label = "Set edge length"
+    bl_description = "change One selected edge length"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    old_length = StringProperty(name = 'originary length') #, default = 0.00, unit = 'LENGTH', precision = 5, set = print(''))
+    target_length = FloatProperty(name = 'length', default = 0.00, unit = 'LENGTH', precision = 5)
+    
+    #incremental = BoolProperty(\
+    #    name="incremental",\
+    #    default=False,\
+    #    description="incremental")
+
+    mode = EnumProperty(
+        items = [
+                 ('fixed', 'fixed', 'fixed'),        
+                 ('increment', 'increment', 'increment'), 
+                 ('decrement', 'decrement', 'decrement'),                  
+                 ],
+        name = "mode")
+
+    behaviour = EnumProperty(
+        items = [
+                 ('proportional', 'proportional', 'Three'),        
+                 #('invert', 'invert', 'Three'),
+                 ('clockwise', 'clockwise', 'One'), 
+                 ('unclockwise', 'unclockwise', 'One'),                  
+                 ],
+        name = "Resize behaviour")
+            
+    originary_edge_length_dict = {}
+    
+    @classmethod
+    def poll(cls, context):
+        return (context.edit_object)
+    
+    def invoke(self, context, event):
+        wm = context.window_manager
+
+        obj = context.edit_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        
+        bpy.ops.mesh.select_mode(type="EDGE")
+        
+        self.selected_edges = get_selected(bm, 'edges')
+        
+        if self.selected_edges:
+            
+            vertex_set = []
+            
+            for edge in self.selected_edges:
+                vector = get_edge_vector( edge )
+                
+                if edge.verts[0].index not in vertex_set:
+                    vertex_set.append( edge.verts[0].index )
+                else: 
+                    self.report( {'ERROR_INVALID_INPUT'}, 'edges with shared vertices not permitted. Use scale instead.' )
+                    return {'CANCELLED'} 
+                if edge.verts[1].index not in vertex_set:
+                    vertex_set.append( edge.verts[1].index )
+                else: 
+                    self.report( {'ERROR_INVALID_INPUT'}, 'edges with shared vertices not permitted. Use scale instead.' )
+                    return {'CANCELLED'} 
+                
+                # warning, it's a constant !
+                verts_index = ''.join((str(edge.verts[0].index), str(edge.verts[1].index)))
+                self.originary_edge_length_dict[ verts_index ] = vector
+                self.old_length = str(vector.length)
+        else:
+            self.report({'ERROR'}, _error_message)
+            return {'CANCELLED'}        
+
+        if edge_length_debug: self.report({'INFO'}, str(self.originary_edge_length_dict)) 
+        
+        if bpy.context.scene.unit_settings.system == 'IMPERIAL':
+            # imperial conversion 2 metre conversion
+            vector.length = ( 0.9144 * vector.length ) / 3
+
+        self.target_length = vector.length
+
+        return wm.invoke_props_dialog(self)
+    
+
+    def execute(self, context):
+        if edge_length_debug: self.report({'INFO'}, 'execute')
+        
+        bpy.ops.mesh.select_mode(type="EDGE")
+
+        self.context = context
+        
+        obj = context.edit_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        
+        self.selected_edges = get_selected(bm, 'edges')
+        
+        if not self.selected_edges:
+            self.report({'ERROR'}, _error_message)
+            return {'CANCELLED'}
+
+        for edge in self.selected_edges:
+            
+            vector = get_edge_vector( edge )
+            # what we shold see in originary length dialog field
+            self.old_length = str(vector.length)
+                        
+            vector.length = abs(self.target_length)
+            center_vector = get_center_vector( ( edge.verts[0].co, edge.verts[1].co) )
+            
+            verts_index = ''.join((str(edge.verts[0].index), str(edge.verts[1].index)))
+            
+            if edge_length_debug: self.report({'INFO'}, \
+                                  ' - '.join( ('vector '+str(vector), \
+                                  'originary_vector '+str(self.originary_edge_length_dict[verts_index])\
+                                  ))) 
+
+            verts = ( edge.verts[0].co, edge.verts[1].co)
+            
+            
+            
+            if edge_length_debug: self.report({'INFO'}, \
+            '\n edge.verts[0].co '+str(verts[0])+\
+            '\n edge.verts[1].co '+str(verts[1])+\
+            '\n vector.length'+ str(vector.length))
+            
+            # the clockwise direction have v1 -> v0, unclockwise v0 -> v1
+            
+            if self.target_length >= 0:
+                if self.behaviour == 'proportional':
+                    edge.verts[1].co = center_vector  + vector / 2
+                    edge.verts[0].co = center_vector  - vector / 2
+                    
+                    if self.mode == 'decrement':
+                        edge.verts[0].co = (center_vector  + vector / 2)  - (self.originary_edge_length_dict[verts_index] / 2 )
+                        edge.verts[1].co = (center_vector  - vector / 2)  + (self.originary_edge_length_dict[verts_index] / 2 )
+                    
+                    elif self.mode == 'increment':
+                        edge.verts[1].co = (center_vector  + vector / 2) +  self.originary_edge_length_dict[verts_index] / 2
+                        edge.verts[0].co = (center_vector  - vector / 2) -  self.originary_edge_length_dict[verts_index] / 2
+
+                elif self.behaviour == 'unclockwise':
+                    if self.mode == 'increment':   
+                        edge.verts[1].co = verts[0]  + ( self.originary_edge_length_dict[verts_index] + vector )
+                    elif self.mode == 'decrement':
+                        edge.verts[0].co = verts[1]  - ( self.originary_edge_length_dict[verts_index] - vector )
+                    else:
+                        edge.verts[1].co = verts[0]   + vector
+  
+                    
+                else:
+                    if self.mode == 'increment': 
+                        edge.verts[0].co = verts[1]  - ( self.originary_edge_length_dict[verts_index]  + vector )                        
+                    elif self.mode == 'decrement':   
+                        edge.verts[1].co = verts[0]  + ( self.originary_edge_length_dict[verts_index] - vector )
+                    else:
+                        edge.verts[0].co = verts[1]  - vector
+
+            
+            if bpy.context.scene.unit_settings.system == 'IMPERIAL':
+                # yard conversion 2 metre conversion
+                #vector.length = ( 3. * vector.length ) / 0.9144
+                # metre 2 yard conversion
+                #vector.length = ( 0.9144 * vector.length ) / 3.                
+                for mvert in edge.verts:
+                    # school time: 0.9144 : 3 = X : mvert
+                    mvert.co = ( 0.9144 * mvert.co ) / 3
+            
+            
+            if edge_length_debug: self.report({'INFO'}, \
+            '\n edge.verts[0].co'+str(verts[0])+\
+            '\n edge.verts[1].co'+str(verts[1])+\
+            '\n vector'+str(vector)+'\n v1 > v0:'+str( (verts[1]>=verts[0]  ) ) )
+            
+            bmesh.update_edit_mesh(obj.data, True)
+        
+        
+        return {'FINISHED'}
+
+        
+def menu_func(self, context):
+    self.layout.operator_context = 'INVOKE_DEFAULT'
+    self.layout.separator()    
+    self.layout.label(text="Edges length:")
+    row = self.layout.row(align=True)
+    row.operator(LengthSet.bl_idname, "Set edges length")
+    
+class addarm_help(bpy.types.Operator):
+       bl_idname = 'help.edge_length'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Select a single edge')
+               layout.label('Change length.')
+
+       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_class(LengthSet)
+    bpy.types.VIEW3D_PT_tools_meshedit.append(menu_func)
+    
+    # edge contextual edit menu ( CTRL + E )
+    bpy.types.VIEW3D_MT_edit_mesh_edges.append(menu_func)
+    
+    # hotkey
+    kc = bpy.context.window_manager.keyconfigs.default.keymaps['Mesh']
+    if LengthSet.bl_idname not in kc.keymap_items:
+        kc.keymap_items.new(LengthSet.bl_idname, 'E', 'PRESS', shift = True, alt = True)
+
+def unregister():
+    bpy.utils.unregister_class(LengthSet)
+    bpy.types.VIEW3D_PT_tools_meshedit.remove(menu_func)
+    bpy.types.VIEW3D_MT_edit_mesh_edges.remove(menu_func)
+
+    # hotkey
+    kc = bpy.context.window_manager.keyconfigs.default.keymaps['Mesh']
+    if LengthSet.bl_idname in kc.keymap_items:
+        kc.keymap_items.remove(kc.keymap_items[LengthSet.bl_idname])
+
+if __name__ == "__main__":
+    register()
diff --git a/mesh_extra_tools/mesh_extras.py b/mesh_extra_tools/mesh_extras.py
deleted file mode 100644 (file)
index 49ddd44..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-import bpy, mathutils, math
-from mathutils import geometry
-
-# Get a matrix for the selected faces that you can use to do local transforms
-def get_selection_matrix(faces=False):
-       
-       me = bpy.context.active_object.data
-       
-       if not faces:
-               faces = get_selected_faces()
-       
-       yVec = mathutils.Vector()
-       zVec = mathutils.Vector()
-       
-       # Ok so we have a basic matrix, but lets base it more on the mesh!
-       for f in faces:
-                       
-               v1 = me.vertices[f.vertices[0]].co
-               v2 = me.vertices[f.vertices[1]].co
-               edge = v2-v1
-               
-               yVec += edge
-               
-               if len(f.vertices) == 4:
-                       v1 = me.vertices[f.vertices[2]].co
-                       v2 = me.vertices[f.vertices[3]].co
-                       edge = v1-v2
-                       
-                       yVec += edge
-               
-               zVec += mathutils.Vector(f.normal)
-                                       
-       if not yVec.length:
-               quat = zVec.to_track_quat('-Z', 'Y')
-               tMat = quat.to_matrix()
-               yVec = tMat[1]
-               yVec = yVec.normalized()
-       else:
-               yVec = yVec.normalized()
-       zVec = zVec.normalized()
-       
-       # Rotate yVec so it's 90 degrees to zVec
-       cross =yVec.cross(zVec)
-       vec = float(yVec.angle(zVec) - math.radians(90))
-       mat = mathutils.Matrix.Rotation(vec, 3, cross)
-       yVec =  (mat * yVec)
-       
-       xVec = yVec.cross(zVec)
-       
-       xVec = xVec.normalized()
-       
-       nMat = mathutils.Matrix((xVec, yVec, zVec))
-       
-       return nMat
-
-
-
-# Get the selection radius (minimum distance of an outer edge to the centre)
-def get_selection_radius():
-
-       ob = bpy.context.active_object
-
-       radius = 0.0
-       
-       # no use continueing if nothing is selected
-       if contains_selected_item(ob.data.polygons):
-       
-               # Find the center of the selection
-               cent = mathutils.Vector()
-               nr = 0
-               nonVerts = []
-               selVerts = []
-               for f in ob.data.polygons:
-                       if f.select:
-                               nr += 1
-                               cent += f.center
-                       else:
-                               nonVerts.extend(f.vertices)
-                               
-               cent /= nr
-               
-               chk = 0
-               
-               # Now that we know the center.. we can figure out how close the nearest point on an outer edge is
-               for e in get_selected_edges():
-               
-                       nonSection = [v for v in e.vertices if v in nonVerts]
-                       if len(nonSection):
-                       
-                               v0 = ob.data.vertices[e.vertices[0]].co
-                               v1 = ob.data.vertices[e.vertices[1]].co
-                               
-                               # If there's more than 1 vert of this edge on the outside... we need the edge length to be long enough too!
-                               if len(nonSection) > 1:
-                                       edge = v0 - v1
-                                       edgeRad = edge.length * 0.5
-                                       
-                                       if edgeRad < radius or not chk:
-                                               radius = edgeRad
-                                               chk += 1
-                               
-                               int = geometry.intersect_point_line(cent, v0, v1)
-                               
-                               rad = cent - int[0]
-                               l = rad.length
-                               
-                               if l < radius or not chk:
-                                       radius = l
-                                       chk += 1
-                                       
-       return radius
-       
-       
-       
-# Get the average length of the outer edges of the current selection
-def get_shortest_outer_edge_length():
-
-       ob = bpy.context.active_object
-
-       min = False
-       me = ob.data
-       
-       delVerts = []
-       for f in me.faces:
-               if not f.select:
-                       delVerts.extend(f.vertices)
-       selEdges = [e.vertices for e in me.edges if e.select]
-
-       if len(selEdges) and len(delVerts):
-               
-               for eVerts in selEdges:
-                       
-                       v0 = eVerts[0]
-                       v1 = eVerts[1]
-                       
-                       if v0 in delVerts and v1 in delVerts:
-                               ln = (me.vertices[v0].co - me.vertices[v1].co).length
-                               if min is False or (ln > 0.0 and ln < min):
-                                       min = ln
-                                               
-       return min
-
-
-# Get the average length of the outer edges of the current selection
-def get_average_outer_edge_length():
-
-       ob = bpy.context.active_object
-
-       ave = 0.0
-       me = ob.data
-       
-       delFaces = [f.vertices for f  in me.polygons if not f.select]
-       selEdges = [e.vertices for e in me.edges if e.select]
-
-       if len(selEdges) and len(delFaces):
-       
-               number = 0
-               
-               for eVerts in selEdges:
-                       
-                       v0 = eVerts[0]
-                       v1 = eVerts[1]
-                       
-                       for fVerts in delFaces:
-                               if v0 in fVerts and v1 in fVerts:
-                                       number += 1
-                                       ave += (me.vertices[v0].co - me.vertices[v1].co).length
-                                       break
-                                               
-               if number:
-                       ave /= number
-                       
-       return ave
-
-
-       
-# Get the selected (or deselected items)
-def get_selected(type='vertices',invert=False):
-       
-       mesh = bpy.context.active_object.data
-       
-       if type == 'vertices':
-               items = mesh.vertices
-       elif type == 'edges':
-               items = mesh.edges
-       else:
-               items = mesh.polygons
-               
-       if invert:
-               L = [i for i in items if not i.select]
-       else:
-               L = [i for i in items if i.select]
-       return L
-       
-       
-       
-# See if the mesh has something selected
-def has_selected(type='vertices',invert=False):
-       
-       mesh = bpy.context.active_object.data
-       
-       if type == 'vertices':
-               items = mesh.vertices
-       elif type == 'edges':
-               items = mesh.edges
-       else:
-               items = mesh.polygons
-               
-       for i in items:
-               if not invert and i.select:
-                       return True
-               elif invert and not i.select:
-                       return True
-                       
-       return False
-               
-               
-
-# Get all the selected vertices (mode is selected or deselected)
-def get_selected_vertices(mode='selected'):
-
-       vertices = bpy.context.active_object.data.vertices
-
-       if mode == 'deselected':
-               L = [v for v in vertices if not v.select]
-       else:
-               L = [v for v in vertices if v.select]
-       return L
-       
-       
-       
-# Get all the selected edges (mode is selected or deselected)
-def get_selected_edges(mode='selected'):
-
-       edges = bpy.context.active_object.data.edges
-
-       if mode == 'deselected':
-               L = [e for e in edges if not e.select]
-       else:
-               L = [e for e in edges if e.select]
-       return L
-
-
-       
-# Get all the selected faces (mode is selected or deselected)
-def get_selected_faces(mode='selected'):
-       
-       polygons = bpy.context.active_object.data.polygons
-       
-       if mode == 'deselected':
-               L = [f for f in polygons if not f.select]
-       else:
-               L = [f for f in polygons if f.select]
-       return L
-       
-       
-       
-# See if there is at least one selected item in 'items'
-def contains_selected_item(items):
-
-       for item in items:
-               if item.select:
-                       return True
-                               
-       return False
-       
-
-
-
-
-
-       
-       
-               
\ No newline at end of file
diff --git a/mesh_extra_tools/mesh_fastloop.py b/mesh_extra_tools/mesh_fastloop.py
new file mode 100644 (file)
index 0000000..8789808
--- /dev/null
@@ -0,0 +1,112 @@
+# ##### 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 #####
+
+bl_info = {
+    "name": "Fast Loop",
+    "description": "Adds loops fast!",
+    "author": "Andy Davies (metalliandy)",
+    "version": (0,16),
+    "blender": (2, 5, 6),
+    "api": 34958,
+    "location": "Tool Shelf",
+    "warning": '', # used for warning icon and text in addons panel
+    "wiki_url": "",
+    "tracker_url": "",
+    "category": "Mesh"}
+    
+"""About this script:-
+This script enables the fast creation of multiple loops on a mesh.
+
+Usage:-
+1)Click the FastLoop button on the Tool Shelf to activate the tool. 
+2)Hover over the mesh in the general area where you would like a loop to be added (shown by a highlight on the mesh).
+3)Click once to confirm the loop placement
+4)place the loop and then slide to fine tune its position.
+5)Repeat 1-4 if needed
+6)Press Esc. twice to exit the tool.
+
+Related Links:-
+http://blenderartists.org/forum/showthread.php?t=206989
+http://www.metalliandy.com
+
+Thanks to:-
+Bartius Crouch (Crouch) - http://sites.google.com/site/bartiuscrouch/
+Dealga McArdle (zeffii) - http://www.digitalaphasia.com
+
+Version history:-
+v0.16 - Ammended script for compatibility with recent API changes.
+v0.15 - Ammended script meta information and button rendering code for compatibility with recent API changes.
+v0.14 - Modal operator.
+v0.13 - Initial revision."""
+
+import bpy
+
+class OBJECT_OT_FastLoop(bpy.types.Operator):
+    bl_idname = "object_ot.fastloop"
+    bl_label = "FastLoop"
+    bl_description = 'Press TAB x 2 to exit'
+
+    active = bpy.props.BoolProperty(name="active", default=False)
+    
+    @classmethod
+    def poll(cls, context):
+        return bpy.ops.mesh.loopcut_slide.poll()
+    
+    def modal(self, context, event):
+        if event.type == 'ESC':
+            context.area.header_text_set()
+            return {'CANCELLED'}
+        elif event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
+            self.active = False
+        
+        if not self.active:
+            self.active = True
+            bpy.ops.mesh.loopcut_slide('INVOKE_DEFAULT')
+            context.area.header_text_set("Press ESC twice to stop FastLoop")
+        
+        return {'RUNNING_MODAL'}
+    
+    def invoke(self, context, event):
+        context.window_manager.modal_handler_add(self)
+        return {'RUNNING_MODAL'}
+
+class fastloop_help(bpy.types.Operator):
+       """ Press TAB x2 to exit """
+       bl_idname = 'help.fastloop'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make an edge or loop selection')
+               layout.label('Create Multiple Edge Loops')
+               layout.label('Press ESC x2 to exit')
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300) 
+## registring
+def register():
+    bpy.utils.register_module(__name__)
+    pass
+
+def unregister():
+    bpy.utils.unregister_module(__name__)
+    pass
+
+if __name__ == "__main__":
+    register()
\ No newline at end of file
index 553420257d7b1070df971046ac4d9f915025a3f6..02709097311a7797f143b232ba2d5770999abdf4 100644 (file)
 # Repeats extrusion + rotation + scale for one or more faces                   #
 
 ################################################################################
-'''
+
 bl_info = {
-    "name": "MExtrude Plus",
+    "name": "MExtrude Plus1",
     "author": "liero",
-    "version": (1, 2, 8),
-    "blender": (2, 71, 0),
+    "version": (1, 2, 9),
+    "blender": (2, 77, 0),
     "location": "View3D > Tool Shelf",
     "description": "Repeat extrusions from faces to create organic shapes",
     "warning": "",
     "wiki_url": "",
     "tracker_url": "https://developer.blender.org/T28570",
     "category": "Mesh"}
-'''
+
 
 import  bpy, bmesh, mathutils, random
 from random import gauss
@@ -54,21 +54,7 @@ def vsca(self, r):
     random.seed(self.ran + r)
     return self.sca * (1 + random.gauss(0, self.var3 / 3))
 
-# centre of a selection of vertex
-def centro(ver):
-    vvv = [v for v in ver if v.select]
-    if not vvv or len(vvv) == len(ver): return ('error')
-    x = sum([round(v.co[0],4) for v in vvv]) / len(vvv)
-    y = sum([round(v.co[1],4) for v in vvv]) / len(vvv)
-    z = sum([round(v.co[2],4) for v in vvv]) / len(vvv)
-    return (x,y,z)
-
-# restore the original state of the object
-def volver(obj, copia, om, msm, msv):
-    for i in copia: obj.data.vertices[i].select = True
-    bpy.context.tool_settings.mesh_select_mode = msm
-    for i in range(len(msv)):
-        obj.modifiers[i].show_viewport = msv[i]
+
 
 class MExtrude(bpy.types.Operator):
     bl_idname = 'object.mextrude'
@@ -180,213 +166,29 @@ class MExtrude(bpy.types.Operator):
         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.')
-
-       def invoke(self, context, event):
-               return context.window_manager.invoke_popup(self, width = 300)
-
-class addarm_help(bpy.types.Operator):
-       bl_idname = 'help.addarm'
-       bl_label = ''
-
-       def draw(self, context):
-               layout = self.layout
-               layout.label('To use:')
-               layout.label('With Multi extrude to rig extrusions.')
-               layout.label('Adds Empty to control rig.')
-               layout.label('Based on selected face/s & object center.')
-
-       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'
-    bl_description = 'Create an armature rig based on mesh selection'
-    bl_options = {'REGISTER', 'UNDO'}
+  bl_idname = 'help.mextrude'
+  bl_label = ''
 
-    numb = IntProperty(name='Max Bones', min=1, max=1000, soft_max=100, default=5, description='Max number of bones')
-    skip = IntProperty(name='Skip Loops', min=0, max=5, default=0, description='Skip some edges to get longer bones')
-    long = FloatProperty(name='Min Length', min=0.01, max=5, default=0.15, description='Discard bones shorter than this value')
-    ika = BoolProperty(name='IK constraints', default=True, description='Add IK constraint and Empty as target')
-    rotk = BoolProperty(name='IK Rotation', default=False, description='IK constraint follows target rotation')
-    auto = BoolProperty(name='Auto weight', default=True, description='Auto weight and assign vertices')
-    env = BoolProperty(name='Envelopes', default=False, description='Use envelopes instead of weights')
-    rad = FloatProperty(name='Radius', min=0.01, max=5, default=0.25, description='Envelope deform radius')
-    nam = StringProperty(name='', default='hueso', description='Default name for bones / groups')
-
-    @classmethod
-    def poll(cls, context):
-        obj = bpy.context.object
-        return (obj and obj.type == 'MESH')
+  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.')
 
-    def draw(self, context):
-        layout = self.layout
-        column = layout.column(align=True)
-        column.prop(self,'numb')
-        column.prop(self,'skip')
-        column.prop(self,'long')
-        column = layout.column(align=True)
-        column.prop(self,'auto')
-        if self.auto:
-            column.prop(self,'env')
-            if self.env: column.prop(self,'rad')
-        column.prop(self,'ika')
-        if self.ika: column.prop(self,'rotk')
-        layout.prop(self,'nam')
+  def invoke(self, context, event):
+    return context.window_manager.invoke_popup(self, width = 300)
+      
 
-    def execute(self, context):
-        scn = bpy.context.scene
-        obj = bpy.context.object
-        fac = obj.data.polygons
-        # save state and selection
-        ver, om = obj.data.vertices, obj.mode
-        msm, msv = list(bpy.context.tool_settings.mesh_select_mode), []
-        for i in range(len(obj.modifiers)):
-            msv.append(obj.modifiers[i].show_viewport)
-            obj.modifiers[i].show_viewport = False
-        bpy.ops.object.mode_set(mode='OBJECT')
-        copia = [v.index for v in ver if v.select]
-        sel = [f.index for f in fac if f.select]
-        bpy.ops.object.mode_set(mode='EDIT')
-        bpy.context.tool_settings.mesh_select_mode = [True, False, False]
-        bpy.ops.mesh.select_all(action='DESELECT')
-        txt = 'Select a face or a vertex where the chain should end...'
-        bpy.ops.object.mode_set(mode='OBJECT')
-
-        # create-from single vertex rig / s and not from faces-
-        if sel == []:
-            sel = ['simple']
-            for i in copia:
-                obj.data.vertices[i].select = True
-
-        # reciclar el rig en cada refresco...
-        try: scn.objects.unlink(rig)
-        except: pass
-
-        # Face Loop
-        for i in sel:
-            if sel[0] != 'simple':
-                for v in ver: v.select = False
-                for v in fac[i].vertices: ver[v].select = True
-            lista = [centro(ver)]
-            if lista[0] == 'error':
-                self.report({'INFO'}, txt)
-                volver(obj, copia, om, msm, msv)
-                return{'FINISHED'}
-
-            # create list of coordinates for bones
-            scn.objects.active = obj
-            for t in range(self.numb):
-                bpy.ops.object.mode_set(mode='EDIT')
-                bpy.ops.object.vertex_group_add()
-                for m in range(self.skip+1):
-                    bpy.ops.mesh.select_more()
-                bpy.ops.object.vertex_group_deselect()
-                bpy.ops.object.mode_set(mode='OBJECT')
-                lista.append(centro(ver))
-                bpy.ops.object.mode_set(mode='EDIT')
-                bpy.ops.object.vertex_group_select()
-                bpy.ops.object.vertex_group_remove()
-                if lista[-1] == 'error':
-                    self.numb = t
-                    lista.pop()
-                    break
-                if len(lista) > 1:
-                    delta = Vector(lista[-2]) - Vector(lista[-1])
-                    if delta.length < self.long:
-                        lista.pop()
-
-            bpy.ops.mesh.select_all(action='DESELECT')
-            bpy.ops.object.mode_set(mode='OBJECT')
-
-            # create and copy armature object transformations
-            lista.reverse()
-            if len(lista) < 2:
-                self.report({'INFO'}, txt)
-                volver(obj, copia, om, msm, msv)
-                return{'FINISHED'}
-            try: arm
-            except:
-                arm = bpy.data.armatures.new('arm')
-                if self.env: arm.draw_type = 'ENVELOPE'
-                else: arm.draw_type = 'STICK'
-                rig = bpy.data.objects.new(obj.name+'_rig', arm)
-                rig.matrix_world = obj.matrix_world
-                if self.env: rig.draw_type = 'WIRE'
-                rig.show_x_ray = True
-                scn.objects.link(rig)
-            scn.objects.active = rig
-            bpy.ops.object.mode_set(mode='EDIT')
-
-            # create the chain of bones from the list
-            for i in range(len(lista)-1):
-                bon = arm.edit_bones.new(self.nam+'.000')
-                bon.use_connect = True
-                bon.tail = lista[i+1]
-                bon.head = lista[i]
-                if self.auto and self.env:
-                    bon.tail_radius = self.rad
-                    bon.head_radius = self.rad
-                if i: bon.parent = padre
-                padre = bon
-            bpy.ops.object.mode_set(mode='OBJECT')
-
-            # IK constraint and create an Empty as target
-            if self.ika:
-                ik = rig.data.bones[-1].name
-                loc = rig.matrix_world * Vector(lista[-1])
-                rot = rig.matrix_world * rig.data.bones[-1].matrix_local
-                bpy.ops.object.add(type='EMPTY', location=loc, rotation=rot.to_euler())
-                tgt = bpy.context.object
-                tgt.name = obj.name+'_target.000'
-                if len(sel) > 1:
-                    try: mega
-                    except:
-                        bpy.ops.object.add(type='EMPTY', location = obj.location)
-                        mega = bpy.context.object
-                        mega.name = obj.name+'_Controls'
-                        tgt.select = True
-                    scn.objects.active = mega
-                    bpy.ops.object.parent_set(type='OBJECT')
-
-                scn.objects.active = rig
-                bpy.ops.object.mode_set(mode='POSE')
-                con = rig.pose.bones[ik].constraints.new('IK')
-                con.target = tgt
-                if self.rotk: con.use_rotation = True
-                tgt.select = False
-                bpy.ops.object.mode_set(mode='OBJECT')
-
-        obj.select = True
-        if self.auto:
-            if self.env: bpy.ops.object.parent_set(type='ARMATURE_ENVELOPE')
-            else: bpy.ops.object.parent_set(type='ARMATURE_AUTO')
-        scn.objects.active = obj
-        volver(obj, copia, om, msm, msv)
-        return{'FINISHED'}
-'''
 def register():
-    bpy.utils.register_class(MExtrude)
+    bpy.utils.register_module(__name__)
 
-    bpy.utils.register_class(BB)
+    #bpy.utils.register_class(BB)
 
 def unregister():
-    bpy.utils.unregister_class(MExtrude)
+    bpy.utils.unregister_module(__name__)
 
-    bpy.utils.unregister_class(BB)
+    #bpy.utils.unregister_class(BB)
 
 
 if __name__ == '__main__':
     register()
-'''
diff --git a/mesh_extra_tools/mesh_normal_smooth.py b/mesh_extra_tools/mesh_normal_smooth.py
deleted file mode 100644 (file)
index 9bea435..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-# mesh_normalsmooth_7.py Copyright (C) 2010, Dolf Veenvliet
-#
-# Relaxes selected vertices while retaining the shape as much as possible
-#
-# ***** 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": "Normal Smooth",
-    "author": "Dolf Veenvliet",
-    "version": (7,),
-    "blender": (2, 63, 0),
-    "location": "View3D > Specials > Normal Smooth ",
-    "description": "Smooth the vertex position based on the normals",
-    "warning": "",
-    "wiki_url": "",
-    "tracker_url": "https://developer.blender.org/T32587",
-    "category": "Mesh"}
-
-"""
-Usage:
-
-Launch from "W-menu" or from "Mesh -> Vertices -> Normal Smooth"
-
-Additional links:
-    Author Site: http://www.macouno.com
-    e-mail: dolf {at} macouno {dot} com
-"""
-
-import bpy, mathutils, math
-from bpy.props import IntProperty
-
-## Rotate one vector (vec1) towards another (vec2)
-## (rad = ammount of degrees to rotate in radians)
-def RotVtoV(vec1, vec2, rad):
-    cross = vec1.cross(vec2)
-    mat = mathutils.Matrix.Rotation(rad, 3, cross)
-    return (mat * vec1)
-
-
-# Find the new coordinate for this verticle
-def smoothVert(v1, v1in, me):
-
-    v1co = v1.co
-    v1no = v1.normal
-
-    # List of verts not to check (don't check against yourself)
-    chk = [v1in]
-    newCo = []
-
-    # Make sure there's faces, otherwise we do nothing
-    if len(me.polygons):
-
-        # Check every face
-        for f in me.polygons:
-
-            # Only check faces that this vert is in
-            if v1in in f.vertices:
-
-                # Loop through all the verts in the face
-                for v2in in f.vertices:
-
-                    # Make sure you check every vert only once
-                    if not v2in in chk:
-
-                        chk.append(v2in)
-
-                        v2 = me.vertices[v2in]
-
-                        v2co = v2.co
-
-                        # Get the vector from one vert to the other
-                        vTov = v2co - v1co
-
-                        vLen = vTov.length
-
-                        # Use half the distance (actually 0.514 seems to be the specific nr to multiply by... just by experience)
-                        vLen *= 0.514
-
-                        # Get the normal rotated 90 degrees (pi * 0.5 = 90 degrees in radians) towards the original vert
-                        vNor = RotVtoV(v2.normal, vTov.normalized(), (math.pi * 0.5))
-
-                        # Make the vector the correct length
-                        vNor = vNor.normalized() * vLen
-
-                        # Add the vector to the vert position to get the correct coord
-                        vNor = v2co + vNor
-
-                        newCo.append(vNor)
-
-    # Calculate the new coord only if there's a result
-    if len(newCo):
-
-        nC = mathutils.Vector()
-
-        # Add all the new coordinates together
-        for c in newCo:
-            nC = nC + c
-
-        # Divide the resulting vector by the total to get the average
-        nC = nC / len(newCo)
-
-    # If there's no result, just return the original coord
-    else:
-        nC = v1co
-
-    return nC
-
-
-# Base function
-def normal_smooth(context):
-
-    ob = context.active_object
-
-    bpy.ops.object.mode_set(mode='OBJECT')
-
-    vNew = {}
-    me = ob.data
-
-    # loop through all verts
-    for v1 in me.vertices:
-
-        # only smooth selected verts
-        if v1.select:
-
-            v1in = v1.index
-
-            # Get the new coords for this vert
-            vNew[v1in] = smoothVert(v1, v1in, me)
-
-    # Only if they're anything new, can we apply anything
-    if len(vNew):
-
-        # Get the indexes for all verts to adapt
-        for k in vNew.keys():
-
-            # Set the vert's new coords
-            me.vertices[k].co = vNew[k]
-
-    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"""
-    bl_idname = 'normal.smooth'
-    bl_label = 'Normal Smooth'
-    bl_options = {'REGISTER', 'UNDO'}
-
-
-    iterations = IntProperty(name="Smoothing iterations",
-                default=1, min=0, max=100, soft_min=0, soft_max=10)
-
-    @classmethod
-    def poll(cls, context):
-        obj = context.active_object
-        return (obj and obj.type == 'MESH')
-
-    def execute(self, context):
-        for i in range(0,self.iterations):
-            normal_smooth(context)
-        return {'FINISHED'}
-
-
-def menu_func(self, context):
-    self.layout.operator(NormalSmooth.bl_idname, text="Normal Smooth")
-
-
-def register():
-    bpy.utils.register_module(__name__)
-
-    bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
-    bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func)
-
-def unregister():
-    bpy.utils.unregister_module(__name__)
-
-    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
-    bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func)
-
-if __name__ == "__main__":
-    register()
diff --git a/mesh_extra_tools/mesh_offset_edges.py b/mesh_extra_tools/mesh_offset_edges.py
new file mode 100644 (file)
index 0000000..63f39b9
--- /dev/null
@@ -0,0 +1,795 @@
+# ***** 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": "Offset Edges",
+    "author": "Hidesato Ikeya",
+    "version": (0, 2, 6),
+    "blender": (2, 70, 0),
+    "location": "VIEW3D > Edge menu(CTRL-E) > Offset Edges",
+    "description": "Offset Edges",
+    "warning": "",
+    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Modeling/offset_edges",
+    "tracker_url": "",
+    "category": "Mesh"}
+
+import math
+from math import sin, cos, pi, copysign, radians
+import bpy
+from bpy_extras import view3d_utils
+import bmesh
+from mathutils import Vector
+from time import perf_counter
+
+X_UP = Vector((1.0, .0, .0))
+Y_UP = Vector((.0, 1.0, .0))
+Z_UP = Vector((.0, .0, 1.0))
+ZERO_VEC = Vector((.0, .0, .0))
+ANGLE_90 = pi / 2
+ANGLE_180 = pi
+ANGLE_360 = 2 * pi
+
+
+def calc_loop_normal(verts, fallback=Z_UP):
+    # Calculate normal from verts using Newell's method.
+    normal = ZERO_VEC.copy()
+
+    if verts[0] is verts[-1]:
+        # Perfect loop
+        range_verts = range(1, len(verts))
+    else:
+        # Half loop
+        range_verts = range(0, len(verts))
+
+    for i in range_verts:
+        v1co, v2co = verts[i-1].co, verts[i].co
+        normal.x += (v1co.y - v2co.y) * (v1co.z + v2co.z)
+        normal.y += (v1co.z - v2co.z) * (v1co.x + v2co.x)
+        normal.z += (v1co.x - v2co.x) * (v1co.y + v2co.y)
+
+    if normal != ZERO_VEC:
+        normal.normalize()
+    else:
+        normal = fallback
+
+    return normal
+
+def collect_edges(bm):
+    set_edges_orig = set()
+    for e in bm.edges:
+        if e.select:
+            co_faces_selected = 0
+            for f in e.link_faces:
+                if f.select:
+                    co_faces_selected += 1
+                    if co_faces_selected == 2:
+                        break
+            else:
+                set_edges_orig.add(e)
+
+    if not set_edges_orig:
+        return None
+
+    return set_edges_orig
+
+def collect_loops(set_edges_orig):
+    set_edges_copy = set_edges_orig.copy()
+
+    loops = []  # [v, e, v, e, ... , e, v]
+    while set_edges_copy:
+        edge_start = set_edges_copy.pop()
+        v_left, v_right = edge_start.verts
+        lp = [v_left, edge_start, v_right]
+        reverse = False
+        while True:
+            edge = None
+            for e in v_right.link_edges:
+                if e in set_edges_copy:
+                    if edge:
+                        # Overlap detected.
+                        return None
+                    edge = e
+                    set_edges_copy.remove(e)
+            if edge:
+                v_right = edge.other_vert(v_right)
+                lp.extend((edge, v_right))
+                continue
+            else:
+                if v_right is v_left:
+                    # Real loop.
+                    loops.append(lp)
+                    break
+                elif reverse is False:
+                    # Right side of half loop.
+                    # Reversing the loop to operate same procedure on the left side.
+                    lp.reverse()
+                    v_right, v_left = v_left, v_right
+                    reverse = True
+                    continue
+                else:
+                    # Half loop, completed.
+                    loops.append(lp)
+                    break
+    return loops
+
+def get_adj_ix(ix_start, vec_edges, half_loop):
+    # Get adjacent edge index, skipping zero length edges
+    len_edges = len(vec_edges)
+    if half_loop:
+        range_right = range(ix_start, len_edges)
+        range_left = range(ix_start-1, -1, -1)
+    else:
+        range_right = range(ix_start, ix_start+len_edges)
+        range_left = range(ix_start-1, ix_start-1-len_edges, -1)
+
+    ix_right = ix_left = None
+    for i in range_right:
+        # Right
+        i %= len_edges
+        if vec_edges[i] != ZERO_VEC:
+            ix_right = i
+            break
+    for i in range_left:
+        # Left
+        i %= len_edges
+        if vec_edges[i] != ZERO_VEC:
+            ix_left = i
+            break
+    if half_loop:
+        # If index of one side is None, assign another index.
+        if ix_right is None:
+            ix_right = ix_left
+        if ix_left is None:
+            ix_left = ix_right
+
+    return ix_right, ix_left
+
+def get_adj_faces(edges):
+    adj_faces = []
+    for e in edges:
+        adj_f = None
+        co_adj = 0
+        for f in e.link_faces:
+            # Search an adjacent face.
+            # Selected face has precedance.
+            if not f.hide and f.normal != ZERO_VEC:
+                adj_exist = True
+                adj_f = f
+                co_adj += 1
+                if f.select:
+                    adj_faces.append(adj_f)
+                    break
+        else:
+            if co_adj == 1:
+                adj_faces.append(adj_f)
+            else:
+                adj_faces.append(None)
+    return adj_faces
+
+
+def get_edge_rail(vert, set_edges_orig):
+    co_edges = co_edges_selected = 0
+    vec_inner = None
+    for e in vert.link_edges:
+        if (e not in set_edges_orig and
+           (e.select or (co_edges_selected == 0 and not e.hide))):
+            v_other = e.other_vert(vert)
+            vec = v_other.co - vert.co
+            if vec != ZERO_VEC:
+                vec_inner = vec
+                if e.select:
+                    co_edges_selected += 1
+                    if co_edges_selected == 2:
+                        return None
+                else:
+                    co_edges += 1
+    if co_edges_selected == 1:
+        vec_inner.normalize()
+        return vec_inner
+    elif co_edges == 1:
+        # No selected edges, one unselected edge.
+        vec_inner.normalize()
+        return vec_inner
+    else:
+        return None
+
+def get_cross_rail(vec_tan, vec_edge_r, vec_edge_l, normal_r, normal_l):
+    # Cross rail is a cross vector between normal_r and normal_l.
+
+    vec_cross = normal_r.cross(normal_l)
+    if vec_cross.dot(vec_tan) < .0:
+        vec_cross *= -1
+    cos_min = min(vec_tan.dot(vec_edge_r), vec_tan.dot(-vec_edge_l))
+    cos = vec_tan.dot(vec_cross)
+    if cos >= cos_min:
+        vec_cross.normalize()
+        return vec_cross
+    else:
+        return None
+
+def move_verts(width, depth, verts, directions, geom_ex):
+    if geom_ex:
+        geom_s = geom_ex['side']
+        verts_ex = []
+        for v in verts:
+            for e in v.link_edges:
+                if e in geom_s:
+                    verts_ex.append(e.other_vert(v))
+                    break
+        #assert len(verts) == len(verts_ex)
+        verts = verts_ex
+
+    for v, (vec_width, vec_depth) in zip(verts, directions):
+        v.co += width * vec_width + depth * vec_depth
+
+def extrude_edges(bm, edges_orig):
+    extruded = bmesh.ops.extrude_edge_only(bm, edges=edges_orig)['geom']
+    n_edges = n_faces = len(edges_orig)
+    n_verts = len(extruded) - n_edges - n_faces
+
+    geom = dict()
+    geom['verts'] = verts = set(extruded[:n_verts])
+    geom['edges'] = edges = set(extruded[n_verts:n_verts + n_edges])
+    geom['faces'] = set(extruded[n_verts + n_edges:])
+    geom['side'] = set(e for v in verts for e in v.link_edges if e not in edges)
+
+    return geom
+
+def clean(bm, mode, edges_orig, geom_ex=None):
+    for f in bm.faces:
+        f.select = False
+    if geom_ex:
+        for e in geom_ex['edges']:
+            e.select = True
+        if mode == 'offset':
+            lis_geom = list(geom_ex['side']) + list(geom_ex['faces'])
+            bmesh.ops.delete(bm, geom=lis_geom, context=2)
+    else:
+        for e in edges_orig:
+            e.select = True
+
+def collect_mirror_planes(edit_object):
+    mirror_planes = []
+    eob_mat_inv = edit_object.matrix_world.inverted()
+    for m in edit_object.modifiers:
+        if (m.type == 'MIRROR' and m.use_mirror_merge):
+            merge_limit = m.merge_threshold
+            if not m.mirror_object:
+                loc = ZERO_VEC
+                norm_x, norm_y, norm_z = X_UP, Y_UP, Z_UP
+            else:
+                mirror_mat_local = eob_mat_inv * m.mirror_object.matrix_world
+                loc = mirror_mat_local.to_translation()
+                norm_x, norm_y, norm_z, _ = mirror_mat_local.adjugated()
+                norm_x = norm_x.to_3d().normalized()
+                norm_y = norm_y.to_3d().normalized()
+                norm_z = norm_z.to_3d().normalized()
+            if m.use_x:
+                mirror_planes.append((loc, norm_x, merge_limit))
+            if m.use_y:
+                mirror_planes.append((loc, norm_y, merge_limit))
+            if m.use_z:
+                mirror_planes.append((loc, norm_z, merge_limit))
+    return mirror_planes
+
+def get_vert_mirror_pairs(set_edges_orig, mirror_planes):
+    if mirror_planes:
+        set_edges_copy = set_edges_orig.copy()
+        vert_mirror_pairs = dict()
+        for e in set_edges_orig:
+            v1, v2 = e.verts
+            for mp in mirror_planes:
+                p_co, p_norm, mlimit = mp
+                v1_dist = abs(p_norm.dot(v1.co - p_co))
+                v2_dist = abs(p_norm.dot(v2.co - p_co))
+                if v1_dist <= mlimit:
+                    # v1 is on a mirror plane.
+                    vert_mirror_pairs[v1] = mp
+                if v2_dist <= mlimit:
+                    # v2 is on a mirror plane.
+                    vert_mirror_pairs[v2] = mp
+                if v1_dist <= mlimit and v2_dist <= mlimit:
+                    # This edge is on a mirror_plane, so should not be offsetted.
+                    set_edges_copy.remove(e)
+        return vert_mirror_pairs, set_edges_copy
+    else:
+        return None, set_edges_orig
+
+def get_mirror_rail(mirror_plane, vec_up):
+    p_norm = mirror_plane[1]
+    mirror_rail = vec_up.cross(p_norm)
+    if mirror_rail != ZERO_VEC:
+        mirror_rail.normalize()
+        # Project vec_up to mirror_plane
+        vec_up = vec_up - vec_up.project(p_norm)
+        vec_up.normalize()
+        return mirror_rail, vec_up
+    else:
+        return None, vec_up
+
+def reorder_loop(verts, edges, lp_normal, adj_faces):
+    for i, adj_f in enumerate(adj_faces):
+        if adj_f is None:
+            continue
+        v1, v2 = verts[i], verts[i+1]
+        e = edges[i]
+        fv = tuple(adj_f.verts)
+        if fv[fv.index(v1)-1] is v2:
+            # Align loop direction
+            verts.reverse()
+            edges.reverse()
+            adj_faces.reverse()
+        if lp_normal.dot(adj_f.normal) < .0:
+            lp_normal *= -1
+        break
+    else:
+        # All elements in adj_faces are None
+        for v in verts:
+            if v.normal != ZERO_VEC:
+                if lp_normal.dot(v.normal) < .0:
+                    verts.reverse()
+                    edges.reverse()
+                    lp_normal *= -1
+                break
+
+    return verts, edges, lp_normal, adj_faces
+
+def get_directions(lp, vec_upward, normal_fallback, vert_mirror_pairs, **options):
+    opt_follow_face = options['follow_face']
+    opt_edge_rail = options['edge_rail']
+    opt_er_only_end = options['edge_rail_only_end']
+    opt_threshold = options['threshold']
+
+    verts, edges = lp[::2], lp[1::2]
+    set_edges = set(edges)
+    lp_normal = calc_loop_normal(verts, fallback=normal_fallback)
+
+    ##### Loop order might be changed below.
+    if lp_normal.dot(vec_upward) < .0:
+        # Make this loop's normal towards vec_upward.
+        verts.reverse()
+        edges.reverse()
+        lp_normal *= -1
+
+    if opt_follow_face:
+        adj_faces = get_adj_faces(edges)
+        verts, edges, lp_normal, adj_faces = \
+            reorder_loop(verts, edges, lp_normal, adj_faces)
+    else:
+        adj_faces = (None, ) * len(edges)
+    ##### Loop order might be changed above.
+
+    vec_edges = tuple((e.other_vert(v).co - v.co).normalized()
+                      for v, e in zip(verts, edges))
+
+    if verts[0] is verts[-1]:
+        # Real loop. Popping last vertex.
+        verts.pop()
+        HALF_LOOP = False
+    else:
+        # Half loop
+        HALF_LOOP = True
+
+    len_verts = len(verts)
+    directions = []
+    for i in range(len_verts):
+        vert = verts[i]
+        ix_right, ix_left = i, i-1
+
+        VERT_END = False
+        if HALF_LOOP:
+            if i == 0:
+                # First vert
+                ix_left = ix_right
+                VERT_END = True
+            elif i == len_verts - 1:
+                # Last vert
+                ix_right = ix_left
+                VERT_END = True
+
+        edge_right, edge_left = vec_edges[ix_right], vec_edges[ix_left]
+        face_right, face_left = adj_faces[ix_right], adj_faces[ix_left]
+
+        norm_right = face_right.normal if face_right else lp_normal
+        norm_left = face_left.normal if face_left else lp_normal
+        if norm_right.angle(norm_left) > opt_threshold:
+            # Two faces are not flat.
+            two_normals = True
+        else:
+            two_normals = False
+
+        tan_right = edge_right.cross(norm_right).normalized()
+        tan_left = edge_left.cross(norm_left).normalized()
+        tan_avr = (tan_right + tan_left).normalized()
+        norm_avr = (norm_right + norm_left).normalized()
+
+        rail = None
+        if two_normals or opt_edge_rail:
+            # Get edge rail.
+            # edge rail is a vector of an inner edge.
+            if two_normals or (not opt_er_only_end) or VERT_END:
+                rail = get_edge_rail(vert, set_edges)
+        if vert_mirror_pairs and VERT_END:
+            if vert in vert_mirror_pairs:
+                rail, norm_avr = \
+                    get_mirror_rail(vert_mirror_pairs[vert], norm_avr)
+        if (not rail) and two_normals:
+            # Get cross rail.
+            # Cross rail is a cross vector between norm_right and norm_left.
+            rail = get_cross_rail(
+                tan_avr, edge_right, edge_left, norm_right, norm_left)
+        if rail:
+            dot = tan_avr.dot(rail)
+            if dot > .0:
+                tan_avr = rail
+            elif dot < .0:
+                tan_avr = -rail
+
+        vec_plane = norm_avr.cross(tan_avr)
+        e_dot_p_r = edge_right.dot(vec_plane)
+        e_dot_p_l = edge_left.dot(vec_plane)
+        if e_dot_p_r or e_dot_p_l:
+            if e_dot_p_r > e_dot_p_l:
+                vec_edge, e_dot_p = edge_right, e_dot_p_r
+            else:
+                vec_edge, e_dot_p = edge_left, e_dot_p_l
+
+            vec_tan = (tan_avr - tan_avr.project(vec_edge)).normalized()
+            # Make vec_tan perpendicular to vec_edge
+            vec_up = vec_tan.cross(vec_edge)
+
+            vec_width = vec_tan - (vec_tan.dot(vec_plane) / e_dot_p) * vec_edge
+            vec_depth = vec_up - (vec_up.dot(vec_plane) / e_dot_p) * vec_edge
+        else:
+            vec_width = tan_avr
+            vec_depth = norm_avr
+
+        directions.append((vec_width, vec_depth))
+
+    return verts, directions
+
+def use_cashes(self, context):
+    self.caches_valid = True
+
+angle_presets = {'0°': 0,
+                 '15°': radians(15),
+                 '30°': radians(30),
+                 '45°': radians(45),
+                 '60°': radians(60),
+                 '75°': radians(75),
+                 '90°': radians(90),}
+def assign_angle_presets(self, context):
+    use_cashes(self, context)
+    self.angle = angle_presets[self.angle_presets]
+
+class OffsetEdges(bpy.types.Operator):
+    """Offset Edges."""
+    bl_idname = "mesh.offset_edges"
+    bl_label = "Offset Edges"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    geometry_mode = bpy.props.EnumProperty(
+        items=[('offset', "Offset", "Offset edges"),
+               ('extrude', "Extrude", "Extrude edges"),
+               ('move', "Move", "Move selected edges")],
+        name="Geometory mode", default='offset',
+        update=use_cashes)
+    width = bpy.props.FloatProperty(
+        name="Width", default=.2, precision=4, step=1, update=use_cashes)
+    flip_width = bpy.props.BoolProperty(
+        name="Flip Width", default=False,
+        description="Flip width direction", update=use_cashes)
+    depth = bpy.props.FloatProperty(
+        name="Depth", default=.0, precision=4, step=1, update=use_cashes)
+    flip_depth = bpy.props.BoolProperty(
+        name="Flip Depth", default=False,
+        description="Flip depth direction", update=use_cashes)
+    depth_mode = bpy.props.EnumProperty(
+        items=[('angle', "Angle", "Angle"),
+               ('depth', "Depth", "Depth")],
+        name="Depth mode", default='angle', update=use_cashes)
+    angle = bpy.props.FloatProperty(
+        name="Angle", default=0, precision=3, step=.1,
+        min=-2*pi, max=2*pi, subtype='ANGLE',
+        description="Angle", update=use_cashes)
+    flip_angle = bpy.props.BoolProperty(
+        name="Flip Angle", default=False,
+        description="Flip Angle", update=use_cashes)
+    follow_face = bpy.props.BoolProperty(
+        name="Follow Face", default=False,
+        description="Offset along faces around")
+    mirror_modifier = bpy.props.BoolProperty(
+        name="Mirror Modifier", default=False,
+        description="Take into account of Mirror modifier")
+    edge_rail = bpy.props.BoolProperty(
+        name="Edge Rail", default=False,
+        description="Align vertices along inner edges")
+    edge_rail_only_end = bpy.props.BoolProperty(
+        name="Edge Rail Only End", default=False,
+        description="Apply edge rail to end verts only")
+    threshold = bpy.props.FloatProperty(
+        name="Flat Face Threshold", default=radians(0.05), precision=5,
+        step=1.0e-4, subtype='ANGLE',
+        description="If difference of angle between two adjacent faces is "
+                    "below this value, those faces are regarded as flat.",
+        options={'HIDDEN'})
+    caches_valid = bpy.props.BoolProperty(
+        name="Caches Valid", default=False,
+        options={'HIDDEN'})
+    angle_presets = bpy.props.EnumProperty(
+        items=[('0°', "0°", "0°"),
+               ('15°', "15°", "15°"),
+               ('30°', "30°", "30°"),
+               ('45°', "45°", "45°"),
+               ('60°', "60°", "60°"),
+               ('75°', "75°", "75°"),
+               ('90°', "90°", "90°"), ],
+        name="Angle Presets", default='0°',
+        update=assign_angle_presets)
+
+    _cache_offset_infos = None
+    _cache_edges_orig_ixs = None
+
+    @classmethod
+    def poll(self, context):
+        return context.mode == 'EDIT_MESH'
+
+    def draw(self, context):
+        layout = self.layout
+        layout.prop(self, 'geometry_mode', text="")
+        #layout.prop(self, 'geometry_mode', expand=True)
+
+        row = layout.row(align=True)
+        row.prop(self, 'width')
+        row.prop(self, 'flip_width', icon='ARROW_LEFTRIGHT', icon_only=True)
+
+        layout.prop(self, 'depth_mode', expand=True)
+        if self.depth_mode == 'angle':
+            d_mode = 'angle'
+            flip = 'flip_angle'
+        else:
+            d_mode = 'depth'
+            flip = 'flip_depth'
+        row = layout.row(align=True)
+        row.prop(self, d_mode)
+        row.prop(self, flip, icon='ARROW_LEFTRIGHT', icon_only=True)
+        if self.depth_mode == 'angle':
+            layout.prop(self, 'angle_presets', text="Presets", expand=True) 
+
+        layout.separator()
+
+        layout.prop(self, 'follow_face')
+
+        row = layout.row()
+        row.prop(self, 'edge_rail')
+        if self.edge_rail:
+            row.prop(self, 'edge_rail_only_end', text="OnlyEnd", toggle=True)
+
+        layout.prop(self, 'mirror_modifier')
+
+        layout.operator('mesh.offset_edges', text='Repeat')
+
+        if self.follow_face:
+            layout.separator()
+            layout.prop(self, 'threshold', text='Threshold')
+
+
+    def get_offset_infos(self, bm, edit_object):
+        if self.caches_valid and self._cache_offset_infos is not None:
+            # Return None, indicating to use cache.
+            return None, None
+
+        time = perf_counter()
+
+        set_edges_orig = collect_edges(bm)
+        if set_edges_orig is None:
+            self.report({'WARNING'},
+                        "No edges selected.")
+            return False, False
+
+        if self.mirror_modifier:
+            mirror_planes = collect_mirror_planes(edit_object)
+            vert_mirror_pairs, set_edges = \
+                get_vert_mirror_pairs(set_edges_orig, mirror_planes)
+
+            if set_edges:
+                set_edges_orig = set_edges
+            else:
+                #self.report({'WARNING'},
+                #            "All selected edges are on mirror planes.")
+                vert_mirror_pairs = None
+        else:
+            vert_mirror_pairs = None
+
+        loops = collect_loops(set_edges_orig)
+        if loops is None:
+            self.report({'WARNING'},
+                        "Overlap detected. Select non-overlap edge loops")
+            return False, False
+
+        vec_upward = (X_UP + Y_UP + Z_UP).normalized()
+        # vec_upward is used to unify loop normals when follow_face is off.
+        normal_fallback = Z_UP
+        #normal_fallback = Vector(context.region_data.view_matrix[2][:3])
+        # normal_fallback is used when loop normal cannot be calculated.
+
+        follow_face = self.follow_face
+        edge_rail = self.edge_rail
+        er_only_end = self.edge_rail_only_end
+        threshold = self.threshold
+
+        offset_infos = []
+        for lp in loops:
+            verts, directions = get_directions(
+                lp, vec_upward, normal_fallback, vert_mirror_pairs,
+                follow_face=follow_face, edge_rail=edge_rail,
+                edge_rail_only_end=er_only_end,
+                threshold=threshold)
+            if verts:
+                offset_infos.append((verts, directions))
+
+        # Saving caches.
+        self._cache_offset_infos = _cache_offset_infos = []
+        for verts, directions in offset_infos:
+            v_ixs = tuple(v.index for v in verts)
+            _cache_offset_infos.append((v_ixs, directions))
+        self._cache_edges_orig_ixs = tuple(e.index for e in set_edges_orig)
+
+        print("Preparing OffsetEdges: ", perf_counter() - time)
+
+        return offset_infos, set_edges_orig
+
+    def do_offset_and_free(self, bm, me, offset_infos=None, set_edges_orig=None):
+        # If offset_infos is None, use caches.
+        # Makes caches invalid after offset.
+
+        #time = perf_counter()
+
+        if offset_infos is None:
+            # using cache
+            bmverts = tuple(bm.verts)
+            bmedges = tuple(bm.edges)
+            edges_orig = [bmedges[ix] for ix in self._cache_edges_orig_ixs]
+            verts_directions = []
+            for ix_vs, directions in self._cache_offset_infos:
+                verts = tuple(bmverts[ix] for ix in ix_vs)
+                verts_directions.append((verts, directions))
+        else:
+            verts_directions = offset_infos
+            edges_orig = list(set_edges_orig)
+
+        if self.depth_mode == 'angle':
+            w = self.width if not self.flip_width else -self.width
+            angle = self.angle if not self.flip_angle else -self.angle
+            width = w * cos(angle)
+            depth = w * sin(angle)
+        else:
+            width = self.width if not self.flip_width else -self.width
+            depth = self.depth if not self.flip_depth else -self.depth
+
+        # Extrude
+        if self.geometry_mode == 'move':
+            geom_ex = None
+        else:
+            geom_ex = extrude_edges(bm, edges_orig)
+
+        for verts, directions in verts_directions:
+            move_verts(width, depth, verts, directions, geom_ex)
+
+        clean(bm, self.geometry_mode, edges_orig, geom_ex)
+
+        bpy.ops.object.mode_set(mode="OBJECT")
+        bm.to_mesh(me)
+        bpy.ops.object.mode_set(mode="EDIT")
+        bm.free()
+        self.caches_valid = False  # Make caches invalid.
+
+        #print("OffsetEdges offset: ", perf_counter() - time)
+
+    def execute(self, context):
+        # In edit mode
+        edit_object = context.edit_object
+        bpy.ops.object.mode_set(mode="OBJECT")
+
+        me = edit_object.data
+        bm = bmesh.new()
+        bm.from_mesh(me)
+
+        offset_infos, edges_orig = self.get_offset_infos(bm, edit_object)
+        if offset_infos is False:
+            bpy.ops.object.mode_set(mode="EDIT")
+            return {'CANCELLED'}
+
+        self.do_offset_and_free(bm, me, offset_infos, edges_orig)
+
+        return {'FINISHED'}
+
+    def restore_original_and_free(self, context):
+        self.caches_valid = False  # Make caches invalid.
+        context.area.header_text_set()
+
+        me = context.edit_object.data
+        bpy.ops.object.mode_set(mode="OBJECT")
+        self._bm_orig.to_mesh(me)
+        bpy.ops.object.mode_set(mode="EDIT")
+
+        self._bm_orig.free()
+        context.area.header_text_set()
+
+    def invoke(self, context, event):
+        # In edit mode
+        edit_object = context.edit_object
+        me = edit_object.data
+        bpy.ops.object.mode_set(mode="OBJECT")
+        for p in me.polygons:
+            if p.select:
+                self.follow_face = True
+                break
+
+        self.caches_valid = False
+        bpy.ops.object.mode_set(mode="EDIT")
+        return self.execute(context)
+
+class offset_edges_help(bpy.types.Operator):
+       bl_idname = 'help.offset_edges'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of Edges.')
+               layout.label('Extrude, rotate extrusions & more.')
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+
+class OffsetEdgesMenu(bpy.types.Menu):
+    bl_idname = "VIEW3D_MT_edit_mesh_offset_edges"
+    bl_label = "Offset Edges"
+
+    def draw(self, context):
+        layout = self.layout
+        layout.operator_context = 'INVOKE_DEFAULT'
+
+        off = layout.operator('mesh.offset_edges', text='Offset')
+        off.geometry_mode = 'offset'
+
+        ext = layout.operator('mesh.offset_edges', text='Extrude')
+        ext.geometry_mode = 'extrude'
+
+        mov = layout.operator('mesh.offset_edges', text='Move')
+        mov.geometry_mode = 'move'
+
+
+def draw_item(self, context):
+    self.layout.menu("VIEW3D_MT_edit_mesh_offset_edges")
+
+
+def register():
+    bpy.utils.register_module(__name__)
+    bpy.types.VIEW3D_MT_edit_mesh_edges.append(draw_item)
+
+
+def unregister():
+    bpy.utils.unregister_module(__name__)
+    bpy.types.VIEW3D_MT_edit_mesh_edges.remove(draw_item)
+
+
+if __name__ == '__main__':
+    register()
\ No newline at end of file
diff --git a/mesh_extra_tools/mesh_polyredux.py b/mesh_extra_tools/mesh_polyredux.py
deleted file mode 100644 (file)
index ca7a29e..0000000
+++ /dev/null
@@ -1,387 +0,0 @@
-# ***** 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, 63, 0),
-    "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 or selected')
-               layout.label('To Help:')
-               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))
-       
-       
-
diff --git a/mesh_extra_tools/mesh_to_wall.py b/mesh_extra_tools/mesh_to_wall.py
new file mode 100644 (file)
index 0000000..aa35190
--- /dev/null
@@ -0,0 +1,256 @@
+# ##### 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; version 2
+#  of the License.
+#
+#  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 #####
+
+
+bl_info = {
+    "name": "Mesh to wall",
+    "author": "luxuy_BlenderCN",
+    "version": (0.8),
+    "blender": (2, 71, 0),
+    "location": "View3D > EditMode > Mesh",
+    "description": "Make wall from single mesh lines.",
+    "url": "http://luxuy.github.io/BlenderAddons/Mesh-to-wall/Mesh_to_wall.html",
+    "category": "Mesh"} 
+
+import math,mathutils
+from mathutils import Vector
+import bpy,bmesh
+from functools import reduce
+from bpy.props import FloatProperty, IntProperty, BoolProperty,EnumProperty,StringProperty
+
+def link_verts(bm,ver_index):
+    linked_verts=[]
+    bm.verts.ensure_lookup_table()
+    v=bm.verts[ver_index]
+    for e in v.link_edges:
+        linked_verts.append(e.verts[1].index)        
+        linked_verts.append(e.verts[0].index)
+    
+    linked_verts=list(set(linked_verts)-set([ver_index]))
+    
+    return linked_verts 
+def qq_sort(bm,ver_index,vm,wm):
+    verts=link_verts(bm,ver_index)
+    pt0=bm.verts[ver_index].co
+    def ang_2d_sort(x):
+        pt1=bm.verts[x].co
+        vec=vm*wm*pt1-vm*wm*pt0
+        vec=Vector(vec[0:2])
+        ang=vec.angle_signed(Vector((1,0)))
+        
+        if ang<0:
+            return ang+3.1415926*2
+        else:
+            return ang
+    verts=sorted(verts, key=ang_2d_sort)
+    
+    return verts
+def turn_left(bm,v1,v2,vm,wm):
+   
+    
+    links=[v1,v2]
+    size=len(link_verts(bm,links[-1]))
+    
+    verts=qq_sort(bm,links[-1],vm,wm)
+    v=verts.index(links[-2])
+    if v==0:
+        v_nxt=verts[-1]
+    else:
+        v_nxt=verts[v-1]
+    
+    links.append(v_nxt)
+    
+    while not(links[-1]==links[1] and links[-2]==links[0]):# and len(links)<50:
+        
+        
+        verts=qq_sort(bm,links[-1],vm,wm)
+        v=verts.index(links[-2])
+        if v==0:
+            v_nxt=verts[-1]
+        else:
+            v_nxt=verts[v-1]
+        
+        links.append(v_nxt)
+    links.pop()
+    return links
+
+def lp_left(bm,lp,wid,vm,wm):
+    # pass
+    size=len(lp)
+    up=wm.inverted()*vm.inverted()*Vector((0,0,1))
+    lp_off=[]
+    faces=[]
+    for i in range(size-1):
+        if i==0:
+            pt=bm.verts[lp[i]].co
+            pre=bm.verts[lp[-2]].co
+            nxt=bm.verts[lp[1]].co
+            pre_ind=lp[size-2]
+            nxt_ind=lp[1]
+        else:
+            bm.verts.ensure_lookup_table()
+            pt=bm.verts[lp[i]].co
+            pre=bm.verts[lp[i-1]].co
+            nxt=bm.verts[lp[i+1]].co
+            pre_ind=lp[i-1]
+            nxt_ind=lp[i+1]
+            
+        vec1=pt-pre
+        vec2=pt-nxt
+        
+        mid=vec1.normalized()+vec2.normalized()
+        if mid.length<10e-4:
+           
+            up2=Vector((0,0,1))
+            mid=up2.cross(vec1)
+            
+        else:
+            xx=mid.cross(vec1).dot(up)
+           
+            if xx>0:
+                mid.negate()
+        
+        mid.normalize()
+        if pre_ind==nxt_ind:
+            mid=(pt-pre).normalized()
+            q_a= mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(90.0))
+            q_b= mathutils.Quaternion((0.0, 0.0, 1.0), math.radians(-180.0))
+            mid.rotate(q_a)
+            pt1=pt+mid*wid 
+            mid.rotate(q_b)
+            pt2=pt+mid*wid 
+            new_vert_1=bm.verts.new(pt1)
+            new_vert_2=bm.verts.new(pt2)
+            lp_off.append([new_vert_1,new_vert_2])
+        else:
+            ang=mid.angle(pre-pt)
+            
+            vec_len=wid/(math.sin(ang))
+            # print(wid)
+            pt=pt+mid*vec_len
+            new_vert=bm.verts.new(pt)
+            lp_off.append(new_vert)
+    lp_off.append(lp_off[0])
+    bm.verts.index_update()
+    for i in range(len(lp_off)-1):
+        bm.verts.ensure_lookup_table()
+        p1=bm.verts[lp[i]]
+        p2=bm.verts[lp[i+1]]
+        p3=lp_off[i+1]
+        p4=lp_off[i]
+       
+        
+        if isinstance(p3, list):
+           
+            faces.append((p1,p2,p3[0],p4))
+            #faces.append((p3[0],p2,p3[1]))
+        elif isinstance(p4, list):
+           
+            faces.append((p1,p2,p3,p4[1]))
+        else:
+            faces.append((p1,p2,p3,p4))
+        
+    return faces
+        
+#================================================================================
+class MeshtoWall(bpy.types.Operator):
+    bl_idname = "bpt.mesh_to_wall"
+    bl_label = "Mesh to Wall"
+    bl_description = "Top View, Extrude Flat Along Edges"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    wid=FloatProperty(name='Wall width:',default=0.1,min=0.001,max=10)
+    
+    
+    def execute(self, context):
+        bpy.ops.object.mode_set(mode = 'OBJECT')
+        bpy.ops.object.mode_set(mode = 'EDIT') 
+        ob=bpy.context.object
+        bm=bmesh.from_edit_mesh(ob.data)
+        bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.003)
+        bmesh.ops.delete(bm, geom=bm.faces, context=3)
+        context.tool_settings.mesh_select_mode = (True,True,False)
+        
+        v3d = context.space_data
+        rv3d = v3d.region_3d
+        vm=rv3d.view_matrix 
+        wm=ob.matrix_world
+        faces=[]
+        sel=[]
+        for v in bm.verts:
+            sel.append(v.index)
+        bpy.ops.mesh.select_all(action='DESELECT')
+       
+        for j in sel:
+            verts=link_verts(bm,j)
+            
+            if len(verts)>1:
+         
+                for i in verts:
+                    
+                    lp=turn_left(bm,j,i,vm,wm)
+     
+                    bpy.ops.mesh.select_all(action='DESELECT')
+           
+                    faces+=lp_left(bm,lp,self.wid*0.5,vm,wm)
+                    lp=[bm.verts[i] for i in lp]
+                    
+                    lp=lp[1:]
+          
+                    bpy.ops.mesh.select_all(action='DESELECT')
+                    
+                       
+        for f in faces:
+            try:
+                bm.faces.new(f)
+            except:
+                pass
+        bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.003)
+        bm=bmesh.update_edit_mesh(ob.data,1,1)
+        
+        
+        return {'FINISHED'}
+
+class wall_help(bpy.types.Operator):
+       bl_idname = 'help.wall'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Extrude Flat Edges.')
+
+       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(MeshtoWall.bl_idname, text="Mesh to wall")
+
+def register():
+    bpy.utils.register_module(__name__)
+    bpy.types.VIEW3D_MT_edit_mesh.append(menu_func)
+   
+def unregister():
+    bpy.utils.unregister_module(__name__)
+
+if __name__ == "__main__":
+    register()
diff --git a/mesh_extra_tools/pkhg_faces.py b/mesh_extra_tools/pkhg_faces.py
new file mode 100644 (file)
index 0000000..47fe8b9
--- /dev/null
@@ -0,0 +1,867 @@
+bl_info = {
+    "name": "PKHG faces",
+    "author": " PKHG ",
+    "version": (0, 0, 5),
+    "blender": (2, 7, 1),
+    "location": "View3D > Tools > PKHG (tab)",
+    "description": "Faces selected will become added faces of different style",
+    "warning": "not yet finished",
+    "wiki_url": "",
+    "category": "Mesh",
+}
+import bpy
+import bmesh
+from mathutils import Vector, Matrix
+from bpy.props import BoolProperty, StringProperty, IntProperty, FloatProperty, EnumProperty
+
+
+class AddFaces(bpy.types.Operator):
+    """Get parameters and build object with added faces"""
+    bl_idname = "mesh.add_faces_to_object"
+    bl_label = "new FACES: add"
+    bl_options = {'REGISTER', 'UNDO' , 'PRESET'}
+    
+    reverse_faces = BoolProperty(name = "reverse_faces", default = False,\
+                    description = "revert the normal of selected faces")
+    name_source_object = StringProperty(
+        name= "which MESH",
+        description = "lets you chose a mesh",
+        default = "Cube")
+    remove_start_faces = BoolProperty(name = "remove_start_faces", default = True,\
+                                      description = "make a choice, remove or not")
+    base_height = FloatProperty(name="base_height faces", min=-20, \
+                    soft_max=10, max=20, default=0.2,\
+                    description="sets general base_height")
+    
+    use_relative_base_height = BoolProperty(name = "rel.base_height", default = False,\
+                        description = " reletive or absolute base_height")
+    
+    relative_base_height = FloatProperty(name="relative_height", min=-5, \
+                    soft_max=5, max=20, default=0.2,\
+                    description="PKHG>TODO")
+    relative_width = FloatProperty(name="relative_width", min=-5, \
+                    soft_max=5, max=20, default=0.2,\
+                    description="PKHG>TODO")
+    second_height =  FloatProperty(name="2. height", min=-5, \
+                    soft_max=5, max=20, default=0.2,\
+                    description="2. height for this and that")
+    width = FloatProperty(name="wds.faces", min = -20, max=20, default=0.5,\
+                    description="sets general width")
+    repeat_extrude = IntProperty(name = "repeat", min = 1 , \
+                                 soft_max = 5, max = 20,\
+                        description = "for longer base")    
+    move_inside = FloatProperty(name="move inside", min = 0.0,\
+                                max= 1.0, default = 0.5,\
+                description = "how much move to inside")
+    thickness = FloatProperty( name = "thickness", soft_min = 0.01, min = 0,\
+                            soft_max = 5.0, max = 20.0, default = 0 )
+    depth = FloatProperty( name = "depth",min = -5,\
+                           soft_max = 5.0, max = 20.0, default = 0)
+
+    collapse_edges = BoolProperty(name = "make point" , default = False,\
+                        description = "collapse vertices of edges")
+    spike_base_width = FloatProperty(name = "spike_base_width", default = 0.4,\
+                        min = -4.0, soft_max = 1, max = 20,\
+                        description = "base width of a  spike")
+    base_height_inset = FloatProperty(name = "base_height_inset", default = 0.0,\
+                                     min = -5, max = 5,\
+                        description = "to elevate/or neg the ...")
+    top_spike = FloatProperty(name = "top_spike", default = 1.0, min = -10.0, max = 10.0,
+                        description = " the base_height of a spike")
+    top_extra_height = FloatProperty(name = "top_extra_height", default = 0.0, min = -10.0, max = 10.0,
+                        description = " add extra height")
+    step_with_real_spike = BoolProperty(name = "step_with_real_spike", default = False,\
+                                        description = " in stepped a real spike")
+    use_relative = BoolProperty(name = "use_relative", default = False,\
+                                description = "change size using area, min of max")
+  
+    '''
+    min_or_max = EnumProperty(
+        description = "use either max area or min area or none",
+        default = "no",
+        items = [
+            ('no', 'no', 'choose one of the other possibilies'),
+            ('maxi', 'maxi', 'use max(areas)'),
+            ('minii', 'mani', 'use min(areas)'),
+        ])
+    '''
+
+    face_types = EnumProperty(
+        description = "different types of faces",
+        default = "no",
+        items = [
+            ('no', 'choose!', 'choose one of the other possibilies'),
+            ('open inset', 'open inset', 'holes'),
+            ('with base', 'with base', 'base and ...'),
+            ('clsd vertical', 'clsd vertical',  'clsd vertical'),
+            ('open vertical', 'open vertical',  'openvertical'),
+            ('spiked', 'spiked', 'spike'),
+            #('open slanted','open slanted','open slanted'), #PKHG>INFO via ...
+            #('clsd point', 'clsd point',  'clsd point'),
+            #('pillow', 'pillow',  'pillow'),
+            ('stepped', 'stepped',  'stepped'),
+            ('boxed', 'boxed',  'boxed'),
+            ('bar', 'bar',  'bar'),
+    ])
+    strange_boxed_effect = BoolProperty(name="strange effect", default=False,
+                        description="do not show one extrusion")
+    use_boundary = BoolProperty(name = "use_boundary", default = True)
+    use_even_offset = BoolProperty(name = "even_offset", default = True)
+    use_relative_offset = BoolProperty(name = "relativ_offset", default = True)
+    use_edge_rail  = BoolProperty(name = "edge_rail", default = False)
+    use_outset = BoolProperty(name = "outset", default = False)
+    use_select_inset = BoolProperty(name = "inset", default = False)
+    #NOT GOOD FOR split use_individual = BoolProperty(name = "individual", default = True)
+    use_interpolate = BoolProperty(name = "interpolate", default = True)
+
+
+    @classmethod
+    def poll(cls, context):
+        #return True
+        result = False
+        active_object = context.active_object
+        if active_object:
+            mesh_objects_name = [el.name for el in bpy.data.objects if el.type ==\
+                             "MESH"]
+            if active_object.name in mesh_objects_name:
+                result = True
+                #if active_object.mode == "OBJECT":
+                    #result = True
+        return result
+        
+    def draw(self, context): #PKHG>INFO Add_Faces_To_Object operator GUI
+        layout = self.layout
+        col = layout.column()
+        col.label(text = "ACTIVE object used!")
+        #col.prop(self, "reverse_faces")
+        #col.prop(self, "remove_start_faces")
+        col.prop(self, "face_types")
+        col.prop(self, "use_relative")
+        if self.face_types == "open inset":
+#            col.prop(self, "remove_start_faces")
+            col.prop(self, "move_inside")
+            col.prop(self, "base_height")
+        elif self.face_types == "with base":
+#            col.prop(self, "remove_start_faces")
+            col.prop(self, "move_inside")
+            col.prop(self, "base_height")
+            col.prop(self, "second_height")
+            col.prop(self, "width")
+        elif self.face_types == "clsd vertical":
+            col.prop(self, "base_height")
+            #col.prop(self, "use_relative_base_height")
+        elif self.face_types == "open vertical":
+            col.prop(self, "base_height")
+            #col.prop(self, "use_relative_base_height")
+        elif self.face_types == "boxed":
+            col.prop(self, "move_inside")
+            col.prop(self, "base_height")
+            col.prop(self, "top_spike")
+            col.prop(self, "strange_boxed_effect")
+        elif self.face_types == "spiked":
+            col.prop(self, "spike_base_width")
+            col.prop(self, "base_height_inset")
+            col.prop(self, "top_spike")
+        elif self.face_types == "bar":
+            #PKHG>INFO not used yet col.prop(self, "base_height_inset")
+#            col.prop(self, "remove_start_faces")
+            col.prop(self, "spike_base_width")
+            col.prop(self, "top_spike")
+            col.prop(self, "top_extra_height")
+            #col.prop(self, "top_relative")
+        elif self.face_types == "stepped":
+            col.prop(self, "spike_base_width")
+            col.prop(self, "base_height_inset")
+            col.prop(self, "top_extra_height")
+            col.prop(self, "second_height")
+            col.prop(self, "step_with_real_spike")
+           
+
+    def execute(self, context):
+        #PKHG>DBG print("\n======== TODO executer of Add_Faces_To_Object L93")
+        bpy.context.scene.objects.active
+        #print("face_types =", self.face_types)
+        obj_name = self.name_source_object
+        face_type = self.face_types
+        if face_type == "spiked":
+            Spiked(spike_base_width = self.spike_base_width,\
+                    base_height_inset = self.base_height_inset,\
+                    top_spike = self.top_spike, top_relative = self.use_relative)
+        elif face_type == "boxed":
+            startinfo = prepare(self, context, self.remove_start_faces)
+            #print(startinfo)
+            bm = startinfo['bm']
+            top = self.top_spike
+            obj = startinfo['obj']
+            obj_matrix_local = obj.matrix_local
+            
+            
+            
+            distance = None
+            base_heights = None
+            t = self.move_inside
+            areas = startinfo['areas']
+            base_height = self.base_height
+
+            #PKHG>INFO relative to size of area?!
+            if self.use_relative:
+                distance = [ min(t * area, 1.0) for i, area  in enumerate(areas)]
+                base_heights = [ base_height * area for i, area  in enumerate(areas)]
+            else:
+                distance = [t] * len(areas)
+                base_heights = [base_height] * len(areas)
+
+            rings = startinfo['rings']
+            centers = startinfo['centers']
+            normals = startinfo['normals']
+            for i in range(len(rings)):
+                make_one_inset(self,context, bm = bm, ringvectors = rings[i],\
+                               center = centers[i], normal = normals[i],\
+                               t = distance[i], base_height = base_heights[i])
+                bpy.ops.mesh.select_mode(type="EDGE")
+                bpy.ops.mesh.select_more()
+                bpy.ops.mesh.select_more()
+            bpy.ops.object.mode_set(mode='OBJECT')
+            #PKHG>INFO base extrusion done and set to the mesh
+        
+            #PKHG>INFO if the extrusion is NOT  done ... it looks straneg soon!
+            if not self.strange_boxed_effect:
+                bpy.ops.object.mode_set(mode='EDIT')
+                obj = context.active_object
+                bm = bmesh.from_edit_mesh(obj.data)
+                bmfaces =  [face for face in bm.faces if face.select]
+                res = extrude_faces(self, context, bm = bm, face_l = bmfaces)
+                ring_edges = [face.edges[:] for face in res]
+                #print("ring_edges L219", ring_edges)
+            
+            bpy.ops.object.mode_set(mode='OBJECT')
+
+            #PKHG>INFO now the extruded facec have to move in normal direction
+            bpy.ops.object.mode_set(mode='EDIT')
+            obj = bpy.context.scene.objects.active
+            bm = bmesh.from_edit_mesh(obj.data)
+            todo_faces = [ face for face in bm.faces if face.select]
+            for face in todo_faces:
+                bmesh.ops.translate( bm, vec = face.normal * top, space = obj_matrix_local,\
+                                     verts = face.verts)
+            bpy.ops.object.mode_set(mode='OBJECT')
+            
+
+        elif face_type == "stepped":
+            Stepped(spike_base_width = self.spike_base_width,\
+                    base_height_inset = self.base_height_inset,\
+                    top_spike = self.second_height,\
+                    top_extra_height = self.top_extra_height,
+                    use_relative_offset = self.use_relative, with_spike = self.step_with_real_spike)
+            
+        elif face_type == "open inset":
+            startinfo = prepare(self, context, self.remove_start_faces)
+            #print(startinfo)
+            bm = startinfo['bm']
+            
+            #PKHG>INFO adjust for relative, via areas
+            t = self.move_inside
+            areas = startinfo['areas']
+            base_height = self.base_height
+            base_heights = None
+            distance = None
+            if self.use_relative:
+                distance = [ min(t * area, 1.0) for i, area  in enumerate(areas)]
+                base_heights = [ base_height * area for i, area  in enumerate(areas)]
+            else:
+                distance = [t] * len(areas)
+                base_heights = [base_height] * len(areas)
+
+            rings = startinfo['rings']
+            centers = startinfo['centers']
+            normals = startinfo['normals']
+            for i in range(len(rings)):
+                make_one_inset(self,context, bm = bm, ringvectors = rings[i],\
+                               center = centers[i], normal = normals[i],\
+                               t = distance[i], base_height = base_heights[i])
+            bpy.ops.object.mode_set(mode='OBJECT')
+
+        elif face_type == "with base":
+            startinfo = prepare(self, context, self.remove_start_faces)
+            #print(startinfo)
+            bm = startinfo['bm']
+            obj = startinfo['obj']
+            object_matrix = obj.matrix_local
+            
+            #PKHG>INFO for relative (using areas)
+            t = self.move_inside
+            areas = startinfo['areas']
+            base_height = self.base_height
+            distance = None
+            base_heights = None
+            if self.use_relative:
+
+                distance = [ min(t * area, 1.0) for i, area  in enumerate(areas)]
+                base_heights = [ base_height * area for i, area  in enumerate(areas)]
+            else:
+                distance = [t] * len(areas)
+                base_heights = [base_height] * len(areas)
+
+            next_rings = []
+            rings = startinfo['rings']
+            centers = startinfo['centers']
+            normals = startinfo['normals']
+            for i in range(len(rings)):
+                next_rings.append(make_one_inset(self,context, bm = bm, ringvectors = rings[i],\
+                               center = centers[i], normal = normals[i],\
+                               t = distance[i], base_height = base_heights[i]))
+
+            prepare_ring = extrude_edges(self,context, bm = bm, edge_l_l = next_rings)
+
+            second_height = self.second_height
+            width = self.width
+            vectors = [[ele.verts[:] for ele in edge] for edge in prepare_ring]
+            n_ring_vecs = []
+            for rings in vectors:
+                v = []
+                for edgv in rings:
+                    v.extend(edgv)
+                #PKHF>INFO no double verts allowed, coming from two adjacents edges!
+                bm.verts.ensure_lookup_table()
+                vv = list(set([ ele.index for ele in v]))
+
+                vvv = [bm.verts[i].co for i in vv]
+                n_ring_vecs.append(vvv)
+            for i, ring in enumerate(n_ring_vecs):
+                make_one_inset(self,context, bm = bm, ringvectors = ring,\
+                               center = centers[i], normal = normals[i],\
+                               t = width, base_height = base_heights[i]+second_height)
+            bpy.ops.object.mode_set(mode='OBJECT')
+
+        else:
+        
+            if face_type == "clsd vertical":
+                #PKHG>DBG print("just extrude")
+                obj_name = context.active_object.name
+                ClosedVertical(name = obj_name,base_height = self.base_height,\
+                               use_relative_base_height = self.use_relative )
+
+            elif face_type == "open vertical":
+                #PKHG>DBG print("just open extrude")
+                obj_name = context.active_object.name
+                OpenVertical(name = obj_name, base_height = self.base_height,\
+                             use_relative_base_height = self.use_relative )
+
+            elif face_type == "bar":
+                startinfo = prepare(self,context, self.remove_start_faces)
+
+                #print(startinfo)
+                #obj = startinfo['obj']
+                #object_matrix = obj.matrix_local
+                #areas = startinfo['areas']
+                #t = self.move_inside
+                #base_height = self.base_height
+                #distance = None
+                #base_heights = None
+                #base_height_inset = self.base_height_inset
+                
+                result = []
+                bm = startinfo['bm']
+                rings = startinfo['rings']
+                centers = startinfo['centers']
+                normals = startinfo['normals']
+                spike_base_width = self.spike_base_width
+                for i,ring  in enumerate(rings):
+                    result.append(make_one_inset(self,context, bm = bm,\
+                                    ringvectors = ring, center = centers[i],\
+                                    normal = normals[i], t = spike_base_width))
+                #PKHG>DBG print("first insets done")
+
+                next_ring_edges_list  = extrude_edges(self,context, bm = bm,\
+                                                      edge_l_l = result)
+                #PKHG>DBG print("rings extruded")
+                top_spike = self.top_spike
+                fac = top_spike
+                object_matrix = startinfo['obj'].matrix_local
+                for i in range(len(next_ring_edges_list)):
+                    translate_ONE_ring(self,context, bm = bm,\
+                                    object_matrix = object_matrix,\
+                                    ring_edges = next_ring_edges_list[i],\
+                                    normal = normals[i], distance = fac )
+                #PKHG>DBG print("rings translated om fac", fac)
+                next_ring_edges_list_2  = extrude_edges(self,context, bm = bm,\
+                                                        edge_l_l = next_ring_edges_list)
+                #PKHG>DBG print("\n\nupper rings are extruded now \n----------------------\n")
+
+                top_extra_height = self.top_extra_height
+                for i in range(len(next_ring_edges_list_2)):
+                    move_corner_vecs_outside(self,context, bm = bm,
+                                             edge_list = next_ring_edges_list_2[i],\
+                                             center = centers[i], normal = normals[i],\
+                                             base_height_erlier = fac + top_extra_height,\
+                                             distance = fac)
+                #PKHG>DBG print("should be now corner moved")
+                bpy.ops.mesh.select_mode(type="VERT")
+                bpy.ops.mesh.select_more()
+
+                bpy.ops.object.mode_set(mode='OBJECT')
+       
+
+        return {'FINISHED'}
+
+class ReverseFacesOperator(bpy.types.Operator):
+    """Reverse selected Faces"""
+    bl_idname = "mesh.revers_selected_faces"
+    bl_label = "reverse normal of selected faces1"
+    bl_options = {'REGISTER', 'UNDO' , 'PRESET'}
+    reverse_faces = BoolProperty(name = "reverse_faces", default = False,\
+                    description = "revert the normal of selected faces")
+    def execute(self, context):
+        name = context.active_object.name
+        ReverseFaces(name = name)
+        return {'FINISHED'}
+   
+class pkhg_help(bpy.types.Operator):
+       bl_idname = 'help.pkhg'
+       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.')
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+               
+class VIEW3D_Faces_Panel(bpy.types.Panel):
+    bl_label = "Face Extrude"
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "TOOLS"
+    bl_category = 'Tools'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    @classmethod
+    def poll(cls, context):
+        #return True
+        result = False
+        active_object = context.active_object
+        if active_object:
+            mesh_objects_name = [el.name for el in bpy.data.objects if el.type ==\
+                             "MESH"]
+            if active_object.name in mesh_objects_name:
+                if active_object.mode == "OBJECT":
+                    result = True
+        return result
+    
+    def draw(self, context):
+        layout = self.layout
+        layout.operator(AddFaces.bl_idname, "Selected Faces!")
+        layout.label("Use this to separate")
+        layout.label("Selected Faces Only")
+        layout.label("In OBJECT mode")
+       
+        layout.operator(ReverseFacesOperator.bl_idname,"Reverse faceNormals")
+
+
+def find_one_ring(sel_vertices):
+    ring0 = sel_vertices.pop(0)
+    #print("eerste weg van sel_vertices???", sel_vertices)
+    #print("ring0" , ring0)
+    #print(sel_vertices)
+    to_delete = []
+    for i, edge in enumerate(sel_vertices):
+        len_nu = len(ring0)
+        if len(ring0 - edge) < len_nu:
+            #print(i, edge)
+            to_delete.append(i)
+            ring0 = ring0.union(edge)
+        
+    #print(ring0)
+    #print(to_delete.sort())
+    to_delete.reverse()
+    #print(to_delete)
+    for el in to_delete:
+        sel_vertices.pop(el)
+    #print(sel_vertices)
+    return (ring0,sel_vertices)
+
+
+class Stepped:
+    def __init__(self, spike_base_width = 0.5, base_height_inset = 0.0, top_spike = 0.2, top_relative = False, top_extra_height = 0 , use_relative_offset = False, with_spike = False):
+        #print("%%%%%%%%%%%%%%%%%%%%%%%%%Class Stepped called \n\n")
+        obj = bpy.context.active_object
+        bpy.ops.object.mode_set(mode='EDIT')
+        bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset=False, use_edge_rail = False, thickness = spike_base_width , depth = 0, use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
+        bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset = use_relative_offset, use_edge_rail = False, thickness = top_extra_height , depth = base_height_inset, use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
+        
+        bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset= use_relative_offset, use_edge_rail = False, thickness = spike_base_width , depth = 0, use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
+        bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset=False, use_edge_rail = False, thickness = 0, depth = top_spike, use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
+        if with_spike:
+            bpy.ops.mesh.merge(type='COLLAPSE')
+        #bm = bmesh.from_edit_mesh(obj.data) 
+        #selected_faces = [face for face in bm.faces if face.select]
+        #PKHF>DBG print("\n\n selected faces = ",selected_faces)
+        #edges_todo = []
+        bpy.ops.object.mode_set(mode='OBJECT')
+
+class Spiked:
+    def __init__(self, spike_base_width = 0.5, base_height_inset = 0.0, top_spike = 0.2, top_relative = False):
+        #print("%%%%%%%%%%%%%%%%%%%%%%%%%Class Spiked called \n\n")
+        obj = bpy.context.active_object
+        bpy.ops.object.mode_set(mode='EDIT')
+        bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset=False, use_edge_rail = False, thickness = spike_base_width , depth = base_height_inset, use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
+        bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset=top_relative, use_edge_rail = False, thickness = 0 , depth = top_spike, use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True)
+        bm = bmesh.from_edit_mesh(obj.data) 
+        selected_faces = [face for face in bm.faces if face.select]
+        #PKHF>DBG print("\n\n selected faces = ",selected_faces)
+        edges_todo = []
+        bpy.ops.mesh.merge(type='COLLAPSE')
+        bpy.ops.object.mode_set(mode='OBJECT')
+
+
+class ClosedVertical:
+    def __init__(self,name = "Plane", base_height = 1, use_relative_base_height = False):
+        obj = bpy.data.objects[name]
+        # print("relative_base_height ", relative_base_height)
+        bm = bmesh.new()
+        bm.from_mesh(obj.data)
+        #PKHG>INFO deselect chosen faces
+        sel = [f for f in bm.faces if f.select]
+        for f in sel: 
+            f.select = False
+        res = bmesh.ops.extrude_discrete_faces(bm,faces=sel )
+        #PKHG>INFO select extruded faces
+        for f in res['faces']: 
+            f.select = True
+           
+        lood = Vector((0,0,1))
+        #PKHG>INFO adjust extrusion by a vector! test just only lood
+        factor = base_height
+        for face  in res['faces']:
+            if use_relative_base_height:
+                area = face.calc_area()
+                #PKHG>DBG print("area of ", face.index, "  is", area)
+                factor = area * base_height
+            else:
+                factor = base_height
+            for el in face.verts:
+                tmp = el.co + face.normal * factor
+                el.co = tmp
+        
+        #me = bpy.data.meshes.new("Mesh")
+        me = bpy.data.meshes[name]
+        bm.to_mesh(me)
+        bm.free()
+
+
+        # Add the mesh to the scene
+        #PKHG>INFO not needed?
+        """
+        scene = bpy.context.scene
+        obj = bpy.data.objects.new("Object", me)
+        scene.objects.link(obj)
+        
+
+        # Select and make active
+        scene.objects.active = obj
+        obj.select = False
+        """
+#bmesh.ops.reverse_faces(bm, faces)
+
+class ReverseFaces:
+    def __init__(self, name = "Cube"):
+        obj = bpy.data.objects[name]
+        me = obj.data
+        bpy.ops.object.mode_set(mode='EDIT')
+        bm = bmesh.new()
+        bm.from_mesh(me)
+        bpy.ops.object.mode_set(mode='OBJECT')
+        sel = [f for f in bm.faces if f.select]
+        #OK so far 
+       
+        #print("sel in ReverseFaces = ", sel)
+        bmesh.ops.reverse_faces(bm, faces = sel)
+        bm.to_mesh(me)
+        bm.free()
+       
+
+class OpenVertical:
+    def __init__(self,name = "Plane", base_height = 1, use_relative_base_height = False):
+
+        obj = bpy.data.objects[name]
+        #PKHG>DBG print("relative_base_height ", relative_base_height)
+        bm = bmesh.new()
+        bm.from_mesh(obj.data)
+        #PKHG>INFO deselect chosen faces
+        sel = [f for f in bm.faces if f.select]
+        for f in sel: 
+            f.select = False
+        res = bmesh.ops.extrude_discrete_faces(bm,faces=sel )
+        #PKHG>INFO select extruded faces
+        for f in res['faces']: 
+            f.select = True
+           
+        #PKHG>INFO adjust extrusion by a vector! test just only lood
+        factor = base_height
+        for face  in res['faces']:
+            if use_relative_base_height:
+                area = face.calc_area()
+                #PKHG>DBG print("area of ", face.index, "  is", area)
+                factor = area * base_height
+            else:
+                factor = base_height
+            for el in face.verts:
+                tmp = el.co + face.normal * factor
+                el.co = tmp
+        
+        #me = bpy.data.meshes.new("Mesh")
+        
+        #bmesh.ops.delete(bm,geom = res['faces'] )
+        me = bpy.data.meshes[name]
+        bm.to_mesh(me)
+        bm.free()
+    
+        bpy.ops.object.editmode_toggle()
+        bpy.ops.mesh.delete(type='FACE')
+        bpy.ops.object.editmode_toggle()
+
+
+        # Add the mesh to the scene
+        #PKHG>INFO not needed?
+        """
+        scene = bpy.context.scene
+        obj = bpy.data.objects.new("Object", me)
+        scene.objects.link(obj)
+        
+
+        # Select and make active
+        scene.objects.active = obj
+        obj.select = False
+        """
+
+        '''
+use_boundary=True, 
+use_even_offset=True, 
+use_relative_offset=False, 
+use_edge_rail=False, 
+use_outset=False, 
+use_select_inset=True, 
+use_individual=False, 
+use_interpolate=True
+        '''
+class StripFaces:
+   #PKHG>OLD def __init__(self,  thickness = 0.5, depth = 0.2, type = 0):
+    def __init__(self, use_boundary = True, use_even_offset=True, use_relative_offset=False, use_edge_rail = True, thickness = 0.0, depth = 0.0, use_outset = False, use_select_inset = False, use_individual = True, use_interpolate = True):
+        '''
+        use_boundary = args[0]
+        use_even_offset = args[1]
+        use_relative_offset = args[2]
+        use_edge_rail = args[3]
+        thickness = args[4]
+        depth = args[5]
+        use_outset = args[6]
+        use_select_inset = args[7] 
+        use_individual = args[8]
+        use_interpolate = args[9]
+        '''
+
+        #print("StripFaces called" )
+        #obj = bpy.data.objects[name]
+        bpy.ops.object.mode_set(mode='EDIT')
+        bpy.ops.mesh.inset(use_boundary = use_boundary, use_even_offset=True, use_relative_offset=False, use_edge_rail = True, thickness = thickness, depth = depth, use_outset=use_outset, use_select_inset=use_select_inset, use_individual=use_individual, use_interpolate=use_interpolate)
+        bpy.ops.object.mode_set(mode='OBJECT')
+        #type = 0
+        #PKHG>IMFO only 3 parameters inc execution context supported!! 
+        #PKHG>IMPOSSIBLE: bpy.ops.mesh.inset(use_boundary , use_even_offset, use_relative_offset, use_edge_rail,thickness, depth, use_outset, use_select_inset,use_individual, use_interpolate)
+        #bpy.ops.mesh.inset(use_boundary = use_boundary, use_even_offset=True, use_relative_offset=False, use_edge_rail = True, thickness = thickness, depth = depth, use_outset=use_outset, use_select_inset=use_select_inset, use_individual=use_individual, use_interpolate=use_interpolate)
+        if False:
+            bpy.ops.mesh.inset(use_boundary, use_even_offset, use_relative_offset, use_edge_rail , thickness, depth, use_outset, use_select_inset, use_individual, use_interpolate)
+        elif type == 0:
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset=False, use_edge_rail = True, thickness = thickness, depth= depth, use_outset=False, use_select_inset=False, use_individual = True , use_interpolate=True)
+        elif type == 1:
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=True, use_relative_offset= False, use_edge_rail = True, thickness = thickness, depth = depth, use_outset=False, use_select_inset=False, use_individual=True, use_interpolate=False)
+            bpy.ops.mesh.delete(type='FACE')           
+
+        elif type == 2:
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=False, use_relative_offset = True, use_edge_rail = True, thickness = thickness, depth = depth, use_outset=False, use_select_inset=False, use_individual=True, use_interpolate=False)
+            bpy.ops.mesh.delete(type='FACE')
+            
+        elif type == 3:
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=False, use_relative_offset = True, use_edge_rail = True, thickness = depth, depth = thickness, use_outset=False, use_select_inset=False, use_individual=True, use_interpolate=True)
+            bpy.ops.mesh.delete(type='FACE')
+        elif type == 4:
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=False, use_relative_offset = True, use_edge_rail = True, thickness = thickness, depth = depth, use_outset = True, use_select_inset=False, use_individual=True, use_interpolate=True)
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=False, use_relative_offset = True, use_edge_rail = True, thickness = thickness , depth = depth , use_outset = True, use_select_inset=False, use_individual=True, use_interpolate=True)
+
+            '''
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=False, use_relative_offset = True, use_edge_rail = True, thickness = 0.5, depth = thickness * 2, use_outset = True, use_select_inset=False, use_individual=True, use_interpolate=True)
+            bpy.ops.mesh.inset(use_boundary = True, use_even_offset=False, use_relative_offset = True, use_edge_rail = True, thickness = 0.5, depth = thickness , use_outset = True, use_select_inset=False, use_individual=True, use_interpolate=True)
+            '''
+            #bpy.ops.mesh.delete(type='FACE')
+
+        bpy.ops.mesh.delete(type='FACE')
+
+        bpy.ops.object.mode_set(mode='OBJECT')
+
+#http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro/Advanced_Tutorials/Python_Scripting/Addon_User_Interface
+def prepare(self, context, remove_start_faces = True):
+    """Start for a face selected change of faces
+       select an object of type mesh, with  activated severel (all) faces
+    """
+    #print("prepare called")
+    obj = bpy.context.scene.objects.active
+    #objmode = obj.mode
+    bpy.ops.object.mode_set(mode='OBJECT')
+    selectedpolygons = [el for el in obj.data.polygons if el.select]
+    #edge_index_list = [[el.index for el in face.edges  ] for face in selectedpolygons]
+    #print("start edge_index_list", edge_index_list)
+    #PKHG>INFO copies of the vectors are needed, otherwise Blender crashes!
+    centers = [face.center for face in selectedpolygons]
+    centers_copy = [Vector((el[0],el[1],el[2])) for el in centers]
+    normals = [face.normal for face in selectedpolygons]
+    normals_copy = [Vector((el[0],el[1],el[2])) for el in normals]
+    vertindicesofpolgons = [[vert for vert in face.vertices] for face in selectedpolygons]
+    vertVectorsOfSelectedFaces = [[obj.data.vertices[ind].co for ind in  vertIndiceofface]\
+                    for vertIndiceofface in  vertindicesofpolgons]
+    vertVectorsOfSelectedFaces_copy = [[Vector((el[0],el[1],el[2])) for el in listofvecs]\
+                         for listofvecs in vertVectorsOfSelectedFaces]
+   
+    bpy.ops.object.mode_set(mode='EDIT')
+    bm = bmesh.from_edit_mesh(obj.data)
+    selected_bm_faces = [ ele for ele in bm.faces if ele.select]
+    selected_edges_per_face_ind = [[ele.index for ele in face.edges] for face in selected_bm_faces]
+    #print("\n\nPKHG>DBG selected_edges_per_face", selected_edges_per_face_ind)
+    indices = [el.index for el in selectedpolygons]
+    #print("indices", indices, bm.faces[:])
+    selected_faces_areas = [bm.faces[:][i] for i in indices ]
+    tmp_area =  [el.calc_area() for el in selected_faces_areas]
+    
+    #PKHG>INFO, selected faces are removed, only their edges are used!
+    if remove_start_faces:    
+        bpy.ops.mesh.delete(type='ONLY_FACE')
+        bpy.ops.object.mode_set(mode='OBJECT')
+        obj.data.update()
+        bpy.ops.object.mode_set(mode='EDIT')
+        bm = bmesh.from_edit_mesh(obj.data)
+        bm.verts.ensure_lookup_table()
+        bm.faces.ensure_lookup_table()
+
+    start_ring_raw = [[bm.verts[ind].index for ind in  vertIndiceofface]  \
+                    for vertIndiceofface in  vertindicesofpolgons]
+    start_ring = []
+    
+    for el in start_ring_raw:
+        #el.sort()
+        start_ring.append(set(el))
+    bm.edges.ensure_lookup_table()
+
+    bm_selected_edges_l_l = [[bm.edges[i] for i in bm_ind_list  ] for bm_ind_list in  selected_edges_per_face_ind]            
+
+    result = {'obj': obj, 'centers':centers_copy, 'normals': normals_copy,\
+              'rings': vertVectorsOfSelectedFaces_copy, 'bm': bm ,\
+              'areas': tmp_area,'startBMRingVerts':start_ring,\
+              'base_edges':bm_selected_edges_l_l}
+    return result
+
+
+
+def make_one_inset(self,context, bm = None, ringvectors = None, center = None,\
+                   normal = None, t = None, base_height = 0):
+    """a face will get  'inserted' faces to create  (normaly) 
+        a hole it t is > 0 and < 1)
+    """
+    tmp = []
+    #PKHG>DBG print("ringvectors", ringvectors,"\ncenter=", center)
+    for el in ringvectors:
+        tmp.append((el * (1 - t) + center * t) + normal * base_height)
+
+    tmp = [bm.verts.new(v) for v in tmp] #the new corner bmvectors
+    #PKHG>INFO so to say sentinells, ot use ONE for ...
+    tmp.append(tmp[0])
+    vectorsFace_i = [bm.verts.new(v) for v in ringvectors]
+    vectorsFace_i.append(vectorsFace_i[0])
+    myres = []
+    for ii in range(len(vectorsFace_i) - 1):
+        #PKHG>INFO next line: sequence important! for added edge 
+        bmvecs = [vectorsFace_i[ii],vectorsFace_i[ii + 1], tmp[ii + 1], tmp[ii]] 
+        res = bm.faces.new(bmvecs)
+        myres.append(res.edges[2])
+        myres[-1].select = True #PKHG>INFO to be used later selected!
+    return (myres)
+
+
+
+
+def extrude_faces(self, context, bm = None, face_l = None):
+    """ 
+       to make a ring extrusion!
+    """
+    all_results = []
+    res = bmesh.ops.extrude_discrete_faces(bm, faces = face_l)['faces']
+    #print(res)
+    for face in res:
+        face.select = True
+    #for face in face_l:
+        #for edge in edge_l:
+        #    edge.select = False
+        
+        #print(res)
+        #tmp = [ ele for ele in res['geom'] if isinstance(ele, bmesh.types.BMFace)]
+        #for edge in tmp:
+        #    edge.select = True
+        #all_results.append(tmp)
+    return res
+
+def extrude_edges(self, context, bm = None, edge_l_l = None):
+    """ 
+       to make a ring extrusion!
+    """
+    all_results = []
+    for edge_l in edge_l_l:
+        for edge in edge_l:
+            edge.select = False
+        res = bmesh.ops.extrude_edge_only(bm, edges = edge_l)
+        tmp = [ ele for ele in res['geom'] if isinstance(ele, bmesh.types.BMEdge)]
+        for edge in tmp:
+            edge.select = True
+        all_results.append(tmp)
+    return all_results
+        
+
+def translate_ONE_ring(self,context, bm = None, object_matrix = None, ring_edges = None ,\
+                       normal = (0,0,1), distance = 0.5):
+    """
+       translate a ring in given (normal?!) direction with given (global) amount
+    """
+    tmp = []
+    for edge in ring_edges:
+        tmp.extend(edge.verts[:])
+    #PKHG>INFO no double vertices allowed by bmesh!
+    tmp = set(tmp)
+    tmp = list(tmp)
+    bmesh.ops.translate(bm, vec = normal * distance, space = object_matrix, verts = tmp)
+    return ring_edges
+    #PKHG>INFO relevant edges will stay selected
+    
+def move_corner_vecs_outside(self, context, bm = None, edge_list = None, center = None, normal = None,\
+                             base_height_erlier = 0.5, distance = 0.5):
+    """
+       move corners (outside meant mostly) dependent on the parameters
+    """
+    tmp = []
+    for edge in edge_list:
+        tmp.extend([ele for ele in edge.verts if isinstance(ele, bmesh.types.BMVert)])
+    #PKHG>INFO to remove vertices, they are all twices used in the ring!
+    tmp = set(tmp)
+    tmp = list(tmp)
+    
+    for i in range(len(tmp)):
+        vec = tmp[i].co
+        direction = vec  + (vec - ( normal * base_height_erlier + center))* distance
+        tmp[i].co = direction
+
+def register():
+    bpy.utils.register_module(__name__)
+
+def unregister():
+    bpy.utils.unregister_module(__name__)
+
+if __name__ == "__main__":
+    register()
diff --git a/mesh_extra_tools/random_vertices.py b/mesh_extra_tools/random_vertices.py
new file mode 100644 (file)
index 0000000..b54ee49
--- /dev/null
@@ -0,0 +1,159 @@
+bl_info = {
+    "name": "Random Vertices",
+    "author": "Oscurart, Greg",
+    "version": (1, 1),
+    "blender": (2, 6, 3),
+    "api": 3900,
+    "location": "Object > Transform > Random Vertices",
+    "description": "Randomize selected components of active object.",
+    "warning": "",
+    "wiki_url": "",
+    "tracker_url": "",
+    "category": "Mesh"}
+
+
+import bpy
+import random
+import bmesh
+
+
+
+
+def add_object(self, context, VALMIN, VALMAX, FACTOR, VGFILTER):
+    
+    ## DISCRIMINA EN LA OPCION CON MAPA DE PESO O NO
+    
+    if VGFILTER == True:
+            
+        ## GENERO VARIABLES
+        MODE=bpy.context.active_object.mode
+        OBJACT=bpy.context.active_object
+        LISTVER=[]
+        
+        ## PASO A EDIT 
+        bpy.ops.object.mode_set(mode='EDIT')
+        
+
+        ## BMESH OBJECT
+        ODATA = bmesh.from_edit_mesh(OBJACT.data)
+        ODATA.select_flush(False)
+        
+        ## SI EL VERTICE ESTA SELECCIONADO LO SUMA A UNA LISTA
+        for vertice in ODATA.verts[:]:
+            if vertice.select:
+                LISTVER.append(vertice.index)
+    
+        ## SI EL VALOR MINIMO ES MAYOR AL MAXIMO, LE SUMA UN VALOR AL MAXIMO
+        if VALMIN[0] >= VALMAX[0]:
+            VALMAX[0] = VALMIN[0] + 1
+            
+        if VALMIN[1] >= VALMAX[1]:
+            VALMAX[1] = VALMIN[1] + 1
+            
+        if VALMIN[2] >= VALMAX[2]:
+            VALMAX[2] = VALMIN[2] + 1                
+            
+        for vertice in LISTVER:
+            if ODATA.verts[vertice].select:
+                VERTEXWEIGHT = OBJACT.data.vertices[vertice].groups[0].weight
+                ODATA.verts[vertice].co=(
+                    (((random.randrange(VALMIN[0],VALMAX[0],1))*VERTEXWEIGHT*FACTOR)/1000)+ODATA.verts[vertice].co[0],
+                    (((random.randrange(VALMIN[1],VALMAX[1],1))*VERTEXWEIGHT*FACTOR)/1000)+ODATA.verts[vertice].co[1],
+                    (((random.randrange(VALMIN[2],VALMAX[2],1))*VERTEXWEIGHT*FACTOR)/1000)+ODATA.verts[vertice].co[2]
+                )
+
+
+    else:
+    
+        if VGFILTER == False:            
+          
+                
+            ## GENERO VARIABLES
+            MODE=bpy.context.active_object.mode
+            OBJACT=bpy.context.active_object
+            LISTVER=[]
+            
+            ## PASO A MODO OBJECT
+            bpy.ops.object.mode_set(mode='EDIT')  
+            
+            ## BMESH OBJECT
+            ODATA = bmesh.from_edit_mesh(OBJACT.data)
+            ODATA.select_flush(False)
+            
+            ## SI EL VERTICE ESTA SELECCIONADO LO SUMA A UNA LISTA
+            for vertice in ODATA.verts[:]:
+                if vertice.select:
+                    LISTVER.append(vertice.index)
+        
+            ## SI EL VALOR MINIMO ES MAYOR AL MAXIMO, LE SUMA UN VALOR AL MAXIMO
+            if VALMIN[0] >= VALMAX[0]:
+                VALMAX[0] = VALMIN[0] + 1
+                
+            if VALMIN[1] >= VALMAX[1]:
+                VALMAX[1] = VALMIN[1] + 1
+                
+            if VALMIN[2] >= VALMAX[2]:
+                VALMAX[2] = VALMIN[2] + 1                
+   
+            for vertice in LISTVER:
+                ODATA.verts.ensure_lookup_table() 
+                if ODATA.verts[vertice].select:
+                    ODATA.verts[vertice].co=(
+                        (((random.randrange(VALMIN[0],VALMAX[0],1))*FACTOR)/1000)+ODATA.verts[vertice].co[0],
+                        (((random.randrange(VALMIN[1],VALMAX[1],1))*FACTOR)/1000)+ODATA.verts[vertice].co[1],
+                        (((random.randrange(VALMIN[2],VALMAX[2],1))*FACTOR)/1000)+ODATA.verts[vertice].co[2]
+                    )
+    
+                        
+    
+class OBJECT_OT_add_object(bpy.types.Operator):
+    """Add a Mesh Object"""
+    bl_idname = "mesh.random_vertices"
+    bl_label = "Random Vertices"
+    bl_description = "Random Vertices"
+    bl_options = {'REGISTER', 'UNDO'}
+    
+    VGFILTER=bpy.props.BoolProperty(name="Vertex Group", default=False)
+    FACTOR=bpy.props.FloatProperty(name="Factor", default=1)
+    VALMIN = bpy.props.IntVectorProperty(name="Min XYZ",default=(0,0,0))
+    VALMAX = bpy.props.IntVectorProperty(name="Max XYZ",default=(1,1,1))
+    def execute(self, context):
+
+        add_object(self, context, self.VALMIN, self.VALMAX, self.FACTOR, self.VGFILTER)
+
+        return {'FINISHED'}
+
+class random_help(bpy.types.Operator):
+       bl_idname = 'help.random_vert'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of Verts.')
+               layout.label('Randomize displaced positions')
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+
+# Registration
+
+def add_oscRandVerts_button(self, context):
+    self.layout.operator(
+        OBJECT_OT_add_object.bl_idname,
+        text="Random Vertices",
+        icon="PLUGIN")
+
+
+def register():
+    bpy.utils.register_class(OBJECT_OT_add_object)
+    bpy.types.VIEW3D_MT_transform.append(add_oscRandVerts_button)
+
+
+def unregister():
+    bpy.utils.unregister_class(OBJECT_OT_add_object)
+    bpy.types.VIEW3D_MT_transform.remove(add_oscRandVerts_button)
+
+
+if __name__ == '__main__':
+    register()
diff --git a/mesh_extra_tools/split_solidify.py b/mesh_extra_tools/split_solidify.py
new file mode 100644 (file)
index 0000000..10c882a
--- /dev/null
@@ -0,0 +1,210 @@
+# -*- 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': 'Split Solidify',
+    'author': 'zmj, updated by zeffii to bmesh ',
+    'version': (0, 1, 2),
+    'blender': (2, 7, 0),
+    'api': 61340,
+    'location': 'View3D > Tool Shelf',
+    'description': '',
+    'warning': '',
+    'wiki_url': '',
+    'tracker_url': '',
+    'category': 'Mesh'}
+
+import bpy
+from bpy.props import EnumProperty, FloatProperty, BoolProperty
+import random
+from math import cos
+import bmesh
+
+
+def f_(self, list_0):
+
+    b_rnd = self.b_rnd
+    rnd = self.rnd
+    opp = self.opp
+    th = self.th
+    b_del = self.b_del
+    en0 = self.en0
+
+    bm = self.bm
+
+    for fi in list_0:
+        bm.faces.ensure_lookup_table()
+        f = bm.faces[fi]
+        list_1 = []
+        list_2 = []
+
+        if b_rnd:
+            d = rnd * random.randrange(0, 10)
+        elif not b_rnd:
+            d = opp
+
+        # add new verts.
+        for vi in f.verts:
+            bm.verts.ensure_lookup_table()
+            v = bm.verts[vi.index]
+
+            if en0 == 'opt0':
+                p1 = (v.co).copy() + ((f.normal).copy() * d)      # out
+                p2 = (v.co).copy() + ((f.normal).copy() * (d - th))      # in
+            elif en0 == 'opt1':
+                ang = ((v.normal).copy()).angle((f.normal).copy())
+                h = th / cos(ang)
+                p1 = (v.co).copy() + ((f.normal).copy() * d)
+                p2 = p1 + (-h * (v.normal).copy())
+
+            v1 = bm.verts.new(p1)
+            v2 = bm.verts.new(p2)
+            v1.select = False
+            v2.select = False
+            list_1.append(v1)
+            list_2.append(v2)
+
+        # add new faces, allows faces with more than 4 verts.
+        n = len(list_1)
+
+        k = bm.faces.new(list_1)
+        k.select = False
+        for i in range(n):
+            j = (i+1) % n
+            vseq = list_1[i], list_2[i], list_2[j], list_1[j]
+            k = bm.faces.new(vseq)
+            k.select = False
+
+        list_2.reverse()
+        k = bm.faces.new(list_2)
+        k.select = False
+
+    bmesh.update_edit_mesh(self.me, True)
+
+'''
+class sp_sol_p0(bpy.types.Panel):
+
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_label = 'Split Solidify'
+    bl_context = 'mesh_edit'
+
+    def draw(self, context):
+        layout = self.layout
+        layout.operator('sp_sol.op0_id', text='Split solidify')
+'''
+
+class sp_sol_op0(bpy.types.Operator):
+
+    bl_idname = 'sp_sol.op0_id'
+    bl_label = 'Split Solidify'
+    bl_description = 'Split & Solidify selected Faces'
+    bl_options = {'REGISTER', 'UNDO'}
+
+    opp = FloatProperty(name='', default=0.4, min=-
+                        100.0, max=100.0, step=1, precision=3)
+    th = FloatProperty(name='', default=0.04, min=-
+                       100.0, max=100.0, step=1, precision=3)
+    rnd = FloatProperty(name='', default=0.06, min=-
+                        10.0, max=10.0, step=1, precision=3)
+
+    b_rnd = BoolProperty(name='Random', default=False)
+    b_del = BoolProperty(name='Delete original faces', default=True)
+
+    en0 = EnumProperty(items=(('opt0', 'Face', ''),
+                              ('opt1', 'Vertex', '')),
+                       name = 'Normal',
+                       default = 'opt0')
+
+    def draw(self, context):
+
+        layout = self.layout
+        layout.label('Normal:')
+        layout.prop(self, 'en0', expand=True)
+        layout.prop(self, 'b_rnd')
+
+        if not self.b_rnd:
+            layout.label('Distance:')
+            layout.prop(self, 'opp')
+        elif self.b_rnd:
+            layout.label('Random distance:')
+            layout.prop(self, 'rnd')
+
+        layout.label('Thickness:')
+        layout.prop(self, 'th')
+        layout.prop(self, 'b_del')
+
+    def execute(self, context):
+
+        obj = bpy.context.active_object
+        self.me = obj.data
+        self.bm = bmesh.from_edit_mesh(self.me)
+        self.me.update()
+#        self.bm.ensure_lookup_table()
+        list_0 = [f.index for f in self.bm.faces if f.select]
+
+        if len(list_0) == 0:
+            self.report({'INFO'}, 'No faces selected')
+            return {'CANCELLED'}
+        elif len(list_0) != 0:
+
+            f_(self, list_0)
+            context.tool_settings.mesh_select_mode = (True, True, True)
+            if self.b_del:
+                bpy.ops.mesh.delete(type='FACE')
+            else:
+                pass
+            return {'FINISHED'}
+
+class solidify_help(bpy.types.Operator):
+       bl_idname = 'help.solidify'
+       bl_label = ''
+
+       def draw(self, context):
+               layout = self.layout
+               layout.label('To use:')
+               layout.label('Make a selection or selection of Faces.')
+               layout.label('Split Faces & Extrude results')
+               layout.label('Similar to a shatter/explode effect')
+
+       def invoke(self, context, event):
+               return context.window_manager.invoke_popup(self, width = 300)
+# ------ ------
+class_list = [sp_sol_op0]
+
+# ------ 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()