diff --git a/mesh_snap_utilities_line.py b/mesh_snap_utilities_line.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e07fa858d0862f2bb3ddc0e5bd975166ab7a66f
--- /dev/null
+++ b/mesh_snap_utilities_line.py
@@ -0,0 +1,1031 @@
+### 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 3
+#  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, see <http://www.gnu.org/licenses/>.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# Contact for more information about the Addon:
+# Email:    germano.costa@ig.com.br
+# Twitter:  wii_mano @mano_wii
+
+bl_info = {
+    "name": "Snap_Utilities_Line",
+    "author": "Germano Cavalcante",
+    "version": (5, 7, 1),
+    "blender": (2, 75, 0),
+    "location": "View3D > TOOLS > Snap Utilities > snap utilities",
+    "description": "Extends Blender Snap controls",
+    "wiki_url" : "http://blenderartists.org/forum/showthread.php?363859-Addon-CAD-Snap-Utilities",
+    "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
+    "category": "Mesh"}
+
+import bpy, bgl, bmesh
+from mathutils import Vector
+from mathutils.geometry import (
+    intersect_point_line,
+    intersect_line_line,
+    intersect_line_plane,
+    intersect_ray_tri)
+
+def get_units_info(scale, unit_system, separate_units):
+    if unit_system == 'METRIC':
+            scale_steps = ((1000, 'km'), (1, 'm'), (1 / 100, 'cm'),
+                (1 / 1000, 'mm'), (1 / 1000000, '\u00b5m'))
+    elif unit_system == 'IMPERIAL':
+            scale_steps = ((5280, 'mi'), (1, '\''),
+                (1 / 12, '"'), (1 / 12000, 'thou'))
+            scale /= 0.3048  # BU to feet
+    else:
+            scale_steps = ((1, ' BU'),)
+            separate_units = False
+
+    return (scale, scale_steps, separate_units)
+
+def convert_distance(val, units_info, precision = 5):
+    scale, scale_steps, separate_units = units_info
+    sval = val * scale
+    idx = 0
+    while idx < len(scale_steps) - 1:
+            if sval >= scale_steps[idx][0]:
+                    break
+            idx += 1
+    factor, suffix = scale_steps[idx]
+    sval /= factor
+    if not separate_units or idx == len(scale_steps) - 1:
+            dval = str(round(sval, precision)) + suffix
+    else:
+            ival = int(sval)
+            dval = str(round(ival, precision)) + suffix
+            fval = sval - ival
+            idx += 1
+            while idx < len(scale_steps):
+                    fval *= scale_steps[idx - 1][0] / scale_steps[idx][0]
+                    if fval >= 1:
+                            dval += ' ' \
+                                + ("%.1f" % fval) \
+                                + scale_steps[idx][1]
+                            break
+                    idx += 1
+    return dval
+
+def location_3d_to_region_2d(region, rv3d, coord):
+    prj = rv3d.perspective_matrix * Vector((coord[0], coord[1], coord[2], 1.0))
+    width_half = region.width / 2.0
+    height_half = region.height / 2.0
+    return Vector((width_half + width_half * (prj.x / prj.w),
+                   height_half + height_half * (prj.y / prj.w),
+                   prj.z / prj.w
+                   ))
+
+def fac_nearest_to_segment_2d(co2v, v2d0, v2d1):
+    u = v2d1.xy - v2d0.xy
+    h = co2v.xy - v2d0.xy
+    return u.dot(h) / u.length_squared
+
+def region_2d_to_orig_and_view_vector(region, rv3d, coord, clamp=None):
+    viewinv = rv3d.view_matrix.inverted()
+    persinv = rv3d.perspective_matrix.inverted()
+
+    dx = (2.0 * coord[0] / region.width) - 1.0
+    dy = (2.0 * coord[1] / region.height) - 1.0
+
+    if rv3d.is_perspective:
+        origin_start = viewinv.translation.copy()
+
+        out = Vector((dx, dy, -0.5))
+
+        w = out.dot(persinv[3].xyz) + persinv[3][3]
+
+        view_vector = ((persinv * out) / w) - origin_start
+    else:
+        view_vector = -viewinv.col[2].xyz
+
+        origin_start = ((persinv.col[0].xyz * dx) +
+                        (persinv.col[1].xyz * dy) +
+                        viewinv.translation)
+
+        if clamp != 0.0:
+            if rv3d.view_perspective != 'CAMERA':
+                # this value is scaled to the far clip already
+                origin_offset = persinv.col[2].xyz
+                if clamp is not None:
+                    if clamp < 0.0:
+                        origin_offset.negate()
+                        clamp = -clamp
+                    if origin_offset.length > clamp:
+                        origin_offset.length = clamp
+
+                origin_start -= origin_offset
+
+    view_vector.normalize()
+    return origin_start, view_vector
+
+def out_Location(rv3d, region, orig, vector):
+    view_matrix = rv3d.view_matrix
+    v1 = Vector((int(view_matrix[0][0]*1.5),int(view_matrix[0][1]*1.5),int(view_matrix[0][2]*1.5)))
+    v2 = Vector((int(view_matrix[1][0]*1.5),int(view_matrix[1][1]*1.5),int(view_matrix[1][2]*1.5)))
+
+    hit = intersect_ray_tri(Vector((1,0,0)), Vector((0,1,0)), Vector(), (vector), (orig), False)
+    if hit == None:
+        hit = intersect_ray_tri(v1, v2, Vector(), (vector), (orig), False)
+    if hit == None:
+        hit = intersect_ray_tri(v1, v2, Vector(), (-vector), (orig), False)
+    if hit == None:
+        hit = Vector()
+    return hit
+
+class SnapCache():
+        bvert = None
+        vco = None
+
+        bedge = None
+        v0 = None
+        v1 = None
+        vmid = None
+        vperp = None
+        v2d0 = None
+        v2d1 = None
+        v2dmid = None
+        v2dperp = None
+
+        bface = None
+        fmid = None
+        fnor = None
+
+        out_obj = None
+        out_obmat = None
+        out_obimat = None
+
+
+def snap_utilities(
+        cache, context, obj_matrix_world,
+        bm, mcursor,
+        outer_verts = False,
+        constrain = None,
+        previous_vert = None,
+        ignore_obj = None,
+        increment = 0.0):
+
+    rv3d = context.region_data
+    region = context.region
+    scene = context.scene
+    is_increment = False
+    r_loc = None
+    r_type = None
+    r_len = 0.0
+    bm_geom = None
+
+    if bm.select_history:
+        bm.select_history[0].select = False
+        bm.select_history.clear()
+
+    bpy.ops.view3d.select(location = (int(mcursor.x), int(mcursor.y)))
+
+    if bm.select_history:
+        bm_geom = bm.select_history[0]
+
+    if isinstance(bm_geom, bmesh.types.BMVert):
+        r_type = 'VERT'
+
+        if cache.bvert != bm_geom:
+            cache.bvert = bm_geom
+            cache.vco = obj_matrix_world * cache.bvert.co
+            #cache.v2d = location_3d_to_region_2d(region, rv3d, cache.vco)
+
+        if constrain:
+            location = intersect_point_line(cache.vco, constrain[0], constrain[1])
+            #factor = location[1]
+            r_loc = location[0]
+        else:
+            r_loc = cache.vco
+
+    elif isinstance(bm_geom, bmesh.types.BMEdge):
+        if cache.bedge != bm_geom:
+            cache.bedge = bm_geom
+            cache.v0 = obj_matrix_world * bm_geom.verts[0].co
+            cache.v1 = obj_matrix_world * bm_geom.verts[1].co
+            cache.vmid = 0.5 * (cache.v0 + cache.v1)
+            cache.v2d0 = location_3d_to_region_2d(region, rv3d, cache.v0)
+            cache.v2d1 = location_3d_to_region_2d(region, rv3d, cache.v1)
+            cache.v2dmid = location_3d_to_region_2d(region, rv3d, cache.vmid)
+
+            if previous_vert and previous_vert not in bm_geom.verts:
+                    pvert_co = obj_matrix_world * previous_vert.co
+                    perp_point = intersect_point_line(pvert_co, cache.v0, cache.v1)
+                    cache.vperp = perp_point[0]
+                    #factor = point_perpendicular[1]
+                    cache.v2dperp = location_3d_to_region_2d(region, rv3d, perp_point[0])
+
+            #else: cache.v2dperp = None
+
+        if constrain:
+            location = intersect_line_line(constrain[0], constrain[1], cache.v0, cache.v1)
+            if location == None:
+                is_increment = True
+                orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
+                end = orig + view_vector
+                location = intersect_line_line(constrain[0], constrain[1], orig, end)
+            r_loc = location[0]
+
+        elif cache.v2dperp and\
+            abs(cache.v2dperp[0] - mcursor[0]) < 10 and abs(cache.v2dperp[1] - mcursor[1]) < 10:
+                r_type = 'PERPENDICULAR'
+                r_loc = cache.vperp
+
+        elif abs(cache.v2dmid[0] - mcursor[0]) < 10 and abs(cache.v2dmid[1] - mcursor[1]) < 10:
+            r_type = 'CENTER'
+            r_loc = cache.vmid
+
+        else:
+            if increment and previous_vert in cache.bedge.verts:
+                is_increment = True
+
+            r_type = 'EDGE'
+            fac = fac_nearest_to_segment_2d(mcursor, cache.v2d0, cache.v2d1)
+            fac *= cache.v2d0.z / cache.v2d1.z #convert to fac3d
+            r_loc = cache.v0 + fac * (cache.v1 - cache.v0)
+
+    elif isinstance(bm_geom, bmesh.types.BMFace):
+        is_increment = True
+        r_type = 'FACE'
+
+        if cache.bface != bm_geom:
+            cache.bface = bm_geom
+            cache.fmid = obj_matrix_world*bm_geom.calc_center_median()
+            cache.fnor = bm_geom.normal*obj_matrix_world.inverted()
+
+        orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
+        end = orig + view_vector
+        r_loc = intersect_line_plane(orig, end, cache.fmid, cache.fnor, False)
+
+        if constrain:
+            is_increment = False
+            r_loc = intersect_point_line(r_loc, constrain[0], constrain[1])[0]
+
+    else: #OUT
+        is_increment = True
+        r_type = 'OUT'
+
+        orig, view_vector = region_2d_to_orig_and_view_vector(region, rv3d, mcursor)
+
+        face_index = -1
+        if cache.out_obj == None:
+            result, r_loc, normal, face_index, cache.out_obj, cache.out_obmat = scene.ray_cast(orig, view_vector)
+            if result:
+                r_type = 'FACE'
+                cache.out_obimat = cache.out_obmat.inverted()
+            else:
+                face_index = -1
+                r_loc = None
+
+        if cache.out_obj and cache.out_obj != ignore_obj:
+            if not r_loc or outer_verts:
+                location = None
+                if face_index == -1:
+                    # get the ray relative to the cache.out_obj
+                    ray_origin_obj = cache.out_obimat * orig
+                    end = orig + view_vector * 1000
+                    ray_target_obj = cache.out_obimat * end
+                    result, location, normal, face_index = cache.out_obj.ray_cast(ray_origin_obj, ray_target_obj)
+
+                if face_index == -1:
+                    cache.out_obj = None
+
+                elif outer_verts:
+                    vloc = None
+                    try:
+                        me = cache.out_obj.data
+                        verts = me.polygons[face_index].vertices
+                        v_dist = 100
+
+                        for i in verts:
+                            v_co = cache.out_obmat * me.vertices[i].co
+                            v_2d = location_3d_to_region_2d(region, rv3d, v_co)
+                            dist = (Vector(mcursor) - v_2d.xy).length_squared
+                            if dist < v_dist:
+                                v_dist = dist
+                                vloc = v_co
+                    except Exception as e:
+                        print('Fail', e)
+
+                    if vloc:
+                        is_increment = False
+                        r_type = 'VERT'
+                        r_loc = vloc
+
+                if not r_loc:
+                    r_type = 'FACE'
+                    r_loc = cache.out_obmat * location
+
+        if constrain:
+            if r_loc:
+                is_increment = False
+                r_loc = intersect_point_line(r_loc, constrain[0], constrain[1])[0]
+            else:
+                r_loc = intersect_line_line(constrain[0], constrain[1], orig, end)[0]
+
+        elif not r_loc:
+            r_loc = out_Location(rv3d, region, orig, view_vector)
+
+    if previous_vert:
+        pv_co = obj_matrix_world * previous_vert.co
+        vec = r_loc - pv_co
+        if is_increment and increment:
+            r_len = round((1 / increment) * vec.length) * increment
+            r_loc = r_len * vec.normalized() + pv_co
+        else:
+            r_len = vec.length
+
+    return r_loc, r_type, bm_geom, r_len
+
+def get_isolated_edges(bmvert):
+    linked = [e for e in bmvert.link_edges if not e.link_faces]
+    for e in linked:
+        linked += [le for v in e.verts if not v.link_faces for le in v.link_edges if le not in linked]
+    return linked
+
+def draw_line(self, obj, Bmesh, bm_geom, location):
+    if not hasattr(self, 'list_verts'):
+        self.list_verts = []
+
+    if not hasattr(self, 'list_edges'):
+        self.list_edges = []
+
+    if not hasattr(self, 'list_faces'):
+        self.list_faces = []
+
+    if bm_geom == None:
+        vertices = (bmesh.ops.create_vert(Bmesh, co=(location)))
+        self.list_verts.append(vertices['vert'][0])
+
+    elif isinstance(bm_geom, bmesh.types.BMVert):
+        if (bm_geom.co - location).length < .01:
+            if self.list_verts == [] or self.list_verts[-1] != bm_geom:
+                self.list_verts.append(bm_geom)
+        else:
+            vertices = bmesh.ops.create_vert(Bmesh, co=(location))
+            self.list_verts.append(vertices['vert'][0])
+
+    elif isinstance(bm_geom, bmesh.types.BMEdge):
+        self.list_edges.append(bm_geom)
+        vector_p0_l = (bm_geom.verts[0].co-location)
+        vector_p1_l = (bm_geom.verts[1].co-location)
+        cross = vector_p0_l.cross(vector_p1_l)/bm_geom.calc_length()
+
+        if cross < Vector((0.001,0,0)): # or round(vector_p0_l.angle(vector_p1_l), 2) == 3.14:
+            factor = vector_p0_l.length/bm_geom.calc_length()
+            vertex0 = bmesh.utils.edge_split(bm_geom, bm_geom.verts[0], factor)
+            self.list_verts.append(vertex0[1])
+            #self.list_edges.append(vertex0[0])
+
+        else: # constrain point is near
+            vertices = bmesh.ops.create_vert(Bmesh, co=(location))
+            self.list_verts.append(vertices['vert'][0])
+
+    elif isinstance(bm_geom, bmesh.types.BMFace):
+        self.list_faces.append(bm_geom)
+        vertices = (bmesh.ops.create_vert(Bmesh, co=(location)))
+        self.list_verts.append(vertices['vert'][0])
+
+    # draw, split and create face
+    if len(self.list_verts) >= 2:
+        V1 = self.list_verts[-2]
+        V2 = self.list_verts[-1]
+        #V2_link_verts = [x for y in [a.verts for a in V2.link_edges] for x in y if x != V2]
+        for edge in V2.link_edges:
+            if V1 in edge.verts:
+                self.list_edges.append(edge)
+                break
+        else: #if V1 not in V2_link_verts:
+            if not V2.link_edges:
+                edge = Bmesh.edges.new([V1, V2])
+                self.list_edges.append(edge)
+            else:
+                link_two_faces = V1.link_faces and V2.link_faces
+                if link_two_faces:
+                    self.list_faces = [f for f in V2.link_faces if f in V1.link_faces]
+
+                elif not self.list_faces:
+                    faces, co2 = (V1.link_faces, V2.co.copy()) if V1.link_faces else (V2.link_faces, V1.co.copy())
+                    for face in faces:
+                        if bmesh.geometry.intersect_face_point(face, co2):
+                            co = co2 - face.calc_center_median()
+                            if co.dot(face.normal) < 0.001:
+                                self.list_faces.append(face)
+
+                if self.list_faces:
+                    edge = Bmesh.edges.new([V1, V2])
+                    self.list_edges.append(edge)
+                    ed_list = get_isolated_edges(V2)
+                    for face in set(self.list_faces):
+                        facesp = bmesh.utils.face_split_edgenet(face, list(set(ed_list)))
+                        self.list_faces = []
+                else:
+                    if self.intersect:
+                        facesp = bmesh.ops.connect_vert_pair(Bmesh, verts = [V1, V2], verts_exclude=Bmesh.verts)
+                        #print(facesp)
+                    if not self.intersect or not facesp['edges']:
+                        edge = Bmesh.edges.new([V1, V2])
+                        self.list_edges.append(edge)
+                    else:
+                        for edge in facesp['edges']:
+                            self.list_edges.append(edge)
+                bmesh.update_edit_mesh(obj.data, tessface=True, destructive=True)
+
+        # create face
+        if self.create_face:
+            ed_list = self.list_edges.copy()
+            for edge in V2.link_edges:
+                for vert in edge.verts:
+                    if vert in self.list_verts:
+                        ed_list.append(edge)
+                        for edge in get_isolated_edges(V2):
+                            if edge not in ed_list:
+                                ed_list.append(edge)
+                        bmesh.ops.edgenet_fill(Bmesh, edges = list(set(ed_list)))
+                        bmesh.update_edit_mesh(obj.data, tessface=True, destructive=True)
+                        break
+            #print('face created')
+
+    return [obj.matrix_world*a.co for a in self.list_verts]
+
+class CharMap:
+    ascii = {
+        ".", ",", "-", "+", "1", "2", "3",
+        "4", "5", "6", "7", "8", "9", "0",
+        "c", "m", "d", "k", "h", "a",
+        " ", "/", "*", "'", "\""
+        #"="
+        }
+    type = {
+        'BACK_SPACE', 'DEL',
+        'LEFT_ARROW', 'RIGHT_ARROW'
+        }
+
+    @staticmethod
+    def modal(self, context, event):
+        c = event.ascii
+        if c:
+            if c == ",":
+                c = "."
+            self.length_entered = self.length_entered[:self.line_pos] + c + self.length_entered[self.line_pos:]
+            self.line_pos += 1
+        if self.length_entered:
+            if event.type == 'BACK_SPACE':
+                self.length_entered = self.length_entered[:self.line_pos-1] + self.length_entered[self.line_pos:]
+                self.line_pos -= 1
+
+            elif event.type == 'DEL':
+                self.length_entered = self.length_entered[:self.line_pos] + self.length_entered[self.line_pos+1:]
+
+            elif event.type == 'LEFT_ARROW':
+                self.line_pos = (self.line_pos - 1) % (len(self.length_entered)+1)
+
+            elif event.type == 'RIGHT_ARROW':
+                self.line_pos = (self.line_pos + 1) % (len(self.length_entered)+1)
+
+class SnapUtilitiesLine(bpy.types.Operator):
+    """ Draw edges. Connect them to split faces."""
+    bl_idname = "mesh.snap_utilities_line"
+    bl_label = "Line Tool"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    constrain_keys = {
+        'X': Vector((1,0,0)),
+        'Y': Vector((0,1,0)),
+        'Z': Vector((0,0,1)),
+        'RIGHT_SHIFT': 'shift',
+        'LEFT_SHIFT': 'shift',
+        }
+
+    @classmethod
+    def poll(cls, context):
+        preferences = context.user_preferences.addons[__name__].preferences
+        return (context.mode in {'EDIT_MESH', 'OBJECT'} and
+                preferences.create_new_obj or
+                (context.object is not None and
+                context.object.type == 'MESH'))
+
+    def modal_navigation(self, context, event):
+        #TO DO:
+        #'View Orbit', 'View Pan', 'NDOF Orbit View', 'NDOF Pan View'
+        rv3d = context.region_data
+        if not hasattr(self, 'navigation_cache'): # or self.navigation_cache == False:
+            #print('update navigation')
+            self.navigation_cache = True
+            self.keys_rotate = set()
+            self.keys_move = set()
+            self.keys_zoom = set()
+            for key in context.window_manager.keyconfigs.user.keymaps['3D View'].keymap_items:
+                if key.idname == 'view3d.rotate':
+                    #self.keys_rotate[key.id]={'Alt': key.alt, 'Ctrl': key.ctrl, 'Shift':key.shift, 'Type':key.type, 'Value':key.value}
+                    self.keys_rotate.add((key.alt, key.ctrl, key.shift, key.type, key.value))
+                if key.idname == 'view3d.move':
+                    self.keys_move.add((key.alt, key.ctrl, key.shift, key.type, key.value))
+                if key.idname == 'view3d.zoom':
+                    self.keys_zoom.add((key.alt, key.ctrl, key.shift, key.type, key.value, key.properties.delta))
+                    if key.type == 'WHEELINMOUSE':
+                        self.keys_zoom.add((key.alt, key.ctrl, key.shift, 'WHEELDOWNMOUSE', key.value, key.properties.delta))
+                    if key.type == 'WHEELOUTMOUSE':
+                        self.keys_zoom.add((key.alt, key.ctrl, key.shift, 'WHEELUPMOUSE', key.value, key.properties.delta))
+
+        evkey = (event.alt, event.ctrl, event.shift, event.type, event.value)
+        if evkey in self.keys_rotate:
+            bpy.ops.view3d.rotate('INVOKE_DEFAULT')
+        elif evkey in self.keys_move:
+            if event.shift and self.vector_constrain and self.vector_constrain[2] in {'RIGHT_SHIFT', 'LEFT_SHIFT', 'shift'}:
+                self.vector_constrain = None
+            bpy.ops.view3d.move('INVOKE_DEFAULT')
+        else:
+            for key in self.keys_zoom:
+                if evkey == key[0:5]:
+                    delta = key[5]
+                    if delta == 0:
+                        bpy.ops.view3d.zoom('INVOKE_DEFAULT')
+                    else:
+                        rv3d.view_distance += delta*rv3d.view_distance/6
+                        rv3d.view_location -= delta*(self.location - rv3d.view_location)/6
+                    break
+
+    def draw_callback_px(self, context):
+        # draw 3d point OpenGL in the 3D View
+        bgl.glEnable(bgl.GL_BLEND)
+        bgl.glDisable(bgl.GL_DEPTH_TEST)
+
+        if self.vector_constrain:
+            vc = self.vector_constrain
+            if hasattr(self, 'preloc') and self.type in {'VERT', 'FACE'}:
+                bgl.glColor4f(1.0,1.0,1.0,0.5)
+                bgl.glPointSize(5)
+                bgl.glBegin(bgl.GL_POINTS)
+                bgl.glVertex3f(*self.preloc)
+                bgl.glEnd()
+            if vc[2] == 'X':
+                Color4f = (self.axis_x_color + (1.0,))
+            elif vc[2] == 'Y':
+                Color4f = (self.axis_y_color + (1.0,))
+            elif vc[2] == 'Z':
+                Color4f = (self.axis_z_color + (1.0,))
+            else:
+                Color4f = self.constrain_shift_color
+        else:
+            if self.type == 'OUT':
+                Color4f = self.out_color
+            elif self.type == 'FACE':
+                Color4f = self.face_color
+            elif self.type == 'EDGE':
+                Color4f = self.edge_color
+            elif self.type == 'VERT':
+                Color4f = self.vert_color
+            elif self.type == 'CENTER':
+                Color4f = self.center_color
+            elif self.type == 'PERPENDICULAR':
+                Color4f = self.perpendicular_color
+
+        bgl.glColor4f(*Color4f)
+        bgl.glPointSize(10)
+        bgl.glBegin(bgl.GL_POINTS)
+        bgl.glVertex3f(*self.location)
+        bgl.glEnd()
+
+        # draw 3d line OpenGL in the 3D View
+        bgl.glEnable(bgl.GL_DEPTH_TEST)
+        bgl.glDepthRange(0,0.9999)
+        bgl.glColor4f(1.0, 0.8, 0.0, 1.0)
+        bgl.glLineWidth(2)
+        bgl.glEnable(bgl.GL_LINE_STIPPLE)
+        bgl.glBegin(bgl.GL_LINE_STRIP)
+        for vert_co in self.list_verts_co:
+            bgl.glVertex3f(*vert_co)
+        bgl.glVertex3f(*self.location)
+        bgl.glEnd()
+
+        # restore opengl defaults
+        bgl.glDepthRange(0,1)
+        bgl.glPointSize(1)
+        bgl.glLineWidth(1)
+        bgl.glDisable(bgl.GL_BLEND)
+        bgl.glDisable(bgl.GL_LINE_STIPPLE)
+        bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
+
+    def modal(self, context, event):
+        context.area.tag_redraw()
+
+        if event.ctrl and event.type == 'Z' and event.value == 'PRESS':
+            bpy.ops.ed.undo()
+            self.vector_constrain = None
+            self.list_verts_co = []
+            self.list_verts = []
+            self.list_edges = []
+            self.list_faces = []
+            self.obj = bpy.context.active_object
+            self.obj_matrix = self.obj.matrix_world.copy()
+            self.bm = bmesh.from_edit_mesh(self.obj.data)
+            return {'RUNNING_MODAL'}
+
+        if event.type == 'MOUSEMOVE' or self.bool_update:
+            if self.rv3d.view_matrix != self.rotMat:
+                self.rotMat = self.rv3d.view_matrix.copy()
+                self.bool_update = True
+                self.cache.bedge = None
+            else:
+                self.bool_update = False
+
+            mval = Vector((event.mouse_region_x, event.mouse_region_y))
+
+            if self.list_verts != []:
+                previous_vert = self.list_verts[-1]
+            else:
+                previous_vert = None
+
+            outer_verts = self.outer_verts and not self.keytab
+            self.location, self.type, self.geom, self.len = snap_utilities(
+                    self.cache, context, self.obj_matrix,
+                    self.bm, mval,
+                    outer_verts = self.outer_verts,
+                    constrain = self.vector_constrain,
+                    previous_vert = previous_vert,
+                    ignore_obj = self.obj,
+                    increment = self.incremental
+            )
+            if self.snap_to_grid and self.type == 'OUT':
+                loc = self.location/self.rd
+                self.location = Vector((round(loc.x),
+                                        round(loc.y),
+                                        round(loc.z)))*self.rd
+
+            if self.keyf8 and self.list_verts_co:
+                lloc = self.list_verts_co[-1]
+                orig, view_vec = region_2d_to_orig_and_view_vector(self.region, self.rv3d, mval)
+                location = intersect_point_line(lloc, orig, (orig+view_vec))
+                vec = (location[0] - lloc)
+                ax, ay, az = abs(vec.x),abs(vec.y),abs(vec.z)
+                vec.x = ax > ay > az or ax > az > ay
+                vec.y = ay > ax > az or ay > az > ax
+                vec.z = az > ay > ax or az > ax > ay
+                if vec == Vector():
+                    self.vector_constrain = None
+                else:
+                    vc = lloc+vec
+                    try:
+                        if vc != self.vector_constrain[1]:
+                            type = 'X' if vec.x else 'Y' if vec.y else 'Z' if vec.z else 'shift'
+                            self.vector_constrain = [lloc, vc, type]
+                    except:
+                        type = 'X' if vec.x else 'Y' if vec.y else 'Z' if vec.z else 'shift'
+                        self.vector_constrain = [lloc, vc, type]
+
+        if event.value == 'PRESS':
+            if self.list_verts_co and (event.ascii in CharMap.ascii or event.type in CharMap.type):
+                CharMap.modal(self, context, event)
+
+            elif event.type in self.constrain_keys:
+                self.bool_update = True
+                if self.vector_constrain and self.vector_constrain[2] == event.type:
+                    self.vector_constrain = ()
+
+                else:
+                    if event.shift:
+                        if isinstance(self.geom, bmesh.types.BMEdge):
+                            if self.list_verts:
+                                loc = self.list_verts_co[-1]
+                                self.vector_constrain = (loc, loc + self.geom.verts[1].co - self.geom.verts[0].co, event.type)
+                            else:
+                                self.vector_constrain = [self.obj_matrix * v.co for v in self.geom.verts]+[event.type]
+                    else:
+                        if self.list_verts:
+                            loc = self.list_verts_co[-1]
+                        else:
+                            loc = self.location
+                        self.vector_constrain = [loc, loc + self.constrain_keys[event.type]]+[event.type]
+
+            elif event.type == 'LEFTMOUSE':
+                # SNAP 2D
+                snap_3d = self.location
+                Lsnap_3d = self.obj_matrix.inverted()*snap_3d
+                Snap_2d = location_3d_to_region_2d(self.region, self.rv3d, snap_3d)
+                if self.vector_constrain and isinstance(self.geom, bmesh.types.BMVert): # SELECT FIRST
+                    bpy.ops.view3d.select(location=(int(Snap_2d[0]), int(Snap_2d[1])))
+                    try:
+                        geom2 = self.bm.select_history[0]
+                    except: # IndexError or AttributeError:
+                        geom2 = None
+                else:
+                    geom2 = self.geom
+                self.vector_constrain = None
+                self.list_verts_co = draw_line(self, self.obj, self.bm, geom2, Lsnap_3d)
+                bpy.ops.ed.undo_push(message="Undo draw line*")
+
+            elif event.type == 'TAB':
+                self.keytab = self.keytab == False
+                if self.keytab:
+                    context.tool_settings.mesh_select_mode = (False, False, True)
+                else:
+                    context.tool_settings.mesh_select_mode = (True, True, True)
+
+            elif event.type == 'F8':
+                self.vector_constrain = None
+                self.keyf8 = self.keyf8 == False
+
+        elif event.value == 'RELEASE':
+            if event.type in {'RET', 'NUMPAD_ENTER'}:
+                if self.length_entered != "" and self.list_verts_co:
+                    try:
+                        text_value = bpy.utils.units.to_value(self.unit_system, 'LENGTH', self.length_entered)
+                        vector = (self.location-self.list_verts_co[-1]).normalized()
+                        location = (self.list_verts_co[-1]+(vector*text_value))
+                        G_location = self.obj_matrix.inverted()*location
+                        self.list_verts_co = draw_line(self, self.obj, self.bm, self.geom, G_location)
+                        self.length_entered = ""
+                        self.vector_constrain = None
+
+                    except:# ValueError:
+                        self.report({'INFO'}, "Operation not supported yet")
+
+            elif event.type in {'RIGHTMOUSE', 'ESC'}:
+                if self.list_verts_co == [] or event.type == 'ESC':
+                    bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
+                    context.tool_settings.mesh_select_mode = self.select_mode
+                    context.area.header_text_set()
+                    context.user_preferences.view.use_rotate_around_active = self.use_rotate_around_active
+                    if not self.is_editmode:
+                        bpy.ops.object.editmode_toggle()
+                    return {'FINISHED'}
+                else:
+                    self.vector_constrain = None
+                    self.list_verts = []
+                    self.list_verts_co = []
+                    self.list_faces = []
+
+        a = ""
+        if self.list_verts_co:
+            if self.length_entered:
+                pos = self.line_pos
+                a = 'length: '+ self.length_entered[:pos] + '|' + self.length_entered[pos:]
+            else:
+                length = self.len
+                length = convert_distance(length, self.uinfo)
+                a = 'length: '+ length
+        context.area.header_text_set("hit: %.3f %.3f %.3f %s" % (self.location[0], self.location[1], self.location[2], a))
+
+        self.modal_navigation(context, event)
+        return {'RUNNING_MODAL'}
+
+    def invoke(self, context, event):
+        if context.space_data.type == 'VIEW_3D':
+            #print('name', __name__, __package__)
+            preferences = context.user_preferences.addons[__name__].preferences
+            create_new_obj = preferences.create_new_obj
+            if context.mode == 'OBJECT' and (create_new_obj or context.object == None or context.object.type != 'MESH'):
+
+                mesh = bpy.data.meshes.new("")
+                obj = bpy.data.objects.new("", mesh)
+                context.scene.objects.link(obj)
+                context.scene.objects.active = obj
+
+            #bgl.glEnable(bgl.GL_POINT_SMOOTH)
+            self.is_editmode = bpy.context.object.data.is_editmode
+            bpy.ops.object.mode_set(mode='EDIT')
+            context.space_data.use_occlude_geometry = True
+
+            self.scale = context.scene.unit_settings.scale_length
+            self.unit_system = context.scene.unit_settings.system
+            self.separate_units = context.scene.unit_settings.use_separate
+            self.uinfo = get_units_info(self.scale, self.unit_system, self.separate_units)
+
+            grid = context.scene.unit_settings.scale_length/context.space_data.grid_scale
+            relative_scale = preferences.relative_scale
+            self.scale = grid/relative_scale
+            self.rd = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(1/self.scale))
+
+            incremental = preferences.incremental
+            self.incremental = bpy.utils.units.to_value(self.unit_system, 'LENGTH', str(incremental))
+
+            self.use_rotate_around_active = context.user_preferences.view.use_rotate_around_active
+            context.user_preferences.view.use_rotate_around_active = True
+
+            self.select_mode = context.tool_settings.mesh_select_mode[:]
+            context.tool_settings.mesh_select_mode = (True, True, True)
+
+            self.region = context.region
+            self.rv3d = context.region_data
+            self.rotMat = self.rv3d.view_matrix.copy()
+            self.obj = bpy.context.active_object
+            self.obj_matrix = self.obj.matrix_world.copy()
+            self.bm = bmesh.from_edit_mesh(self.obj.data)
+            self.cache = SnapCache()
+
+            self.location = Vector()
+            self.list_verts = []
+            self.list_verts_co = []
+            self.bool_update = False
+            self.vector_constrain = ()
+            self.keytab = False
+            self.keyf8 = False
+            self.type = 'OUT'
+            self.len = 0
+            self.length_entered = ""
+            self.line_pos = 0
+
+            self.out_color = preferences.out_color
+            self.face_color = preferences.face_color
+            self.edge_color = preferences.edge_color
+            self.vert_color = preferences.vert_color
+            self.center_color = preferences.center_color
+            self.perpendicular_color = preferences.perpendicular_color
+            self.constrain_shift_color = preferences.constrain_shift_color
+
+            self.axis_x_color = tuple(context.user_preferences.themes[0].user_interface.axis_x)
+            self.axis_y_color = tuple(context.user_preferences.themes[0].user_interface.axis_y)
+            self.axis_z_color = tuple(context.user_preferences.themes[0].user_interface.axis_z)
+
+            self.intersect = preferences.intersect
+            self.create_face = preferences.create_face
+            self.outer_verts = preferences.outer_verts
+            self.snap_to_grid = preferences.increments_grid
+
+            self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, (context,), 'WINDOW', 'POST_VIEW')
+            context.window_manager.modal_handler_add(self)
+            return {'RUNNING_MODAL'}
+        else:
+            self.report({'WARNING'}, "Active space must be a View3d")
+            return {'CANCELLED'}
+
+class PanelSnapUtilities(bpy.types.Panel) :
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "TOOLS"
+    #bl_context = "mesh_edit"
+    bl_category = "Snap Utilities"
+    bl_label = "Snap Utilities"
+
+    @classmethod
+    def poll(cls, context):
+        preferences = context.user_preferences.addons[__name__].preferences
+        return (context.mode in {'EDIT_MESH', 'OBJECT'} and
+                preferences.create_new_obj or
+                (context.object is not None and
+                context.object.type == 'MESH'))
+
+    def draw(self, context):
+        layout = self.layout
+        TheCol = layout.column(align = True)
+        TheCol.operator("mesh.snap_utilities_line", text = "Line", icon="GREASEPENCIL")
+
+        addon_prefs = context.user_preferences.addons[__name__].preferences
+        expand = addon_prefs.expand_snap_settings
+        icon = "TRIA_DOWN" if expand else "TRIA_RIGHT"
+
+        box = layout.box()
+        box.prop(addon_prefs, "expand_snap_settings", icon=icon,
+            text="Settings:", emboss=False)
+        if expand:
+            #box.label(text="Snap Items:")
+            box.prop(addon_prefs, "outer_verts")
+            box.prop(addon_prefs, "incremental")
+            box.prop(addon_prefs, "increments_grid")
+            if addon_prefs.increments_grid:
+                box.prop(addon_prefs, "relative_scale")
+            box.label(text="Line Tool:")
+            box.prop(addon_prefs, "intersect")
+            box.prop(addon_prefs, "create_face")
+            box.prop(addon_prefs, "create_new_obj")
+
+def update_panel(self, context):
+    try:
+        bpy.utils.unregister_class(PanelSnapUtilities)
+    except:
+        pass
+    PanelSnapUtilities.bl_category = context.user_preferences.addons[__name__].preferences.category
+    bpy.utils.register_class(PanelSnapUtilities)
+
+class SnapAddonPreferences(bpy.types.AddonPreferences):
+    # this must match the addon name, use '__package__'
+    # when defining this in a submodule of a python package.
+    bl_idname = __name__
+
+    intersect = bpy.props.BoolProperty(
+            name="Intersect",
+            description="intersects created line with the existing edges, even if the lines do not intersect.",
+            default=True)
+
+    create_new_obj = bpy.props.BoolProperty(
+            name="Create a new object",
+            description="If have not a active object, or the active object is not in edit mode, it creates a new object.",
+            default=False)
+
+    create_face = bpy.props.BoolProperty(
+            name="Create faces",
+            description="Create faces defined by enclosed edges.",
+            default=False)
+
+    outer_verts = bpy.props.BoolProperty(
+            name="Snap to outer vertices",
+            description="The vertices of the objects are not activated also snapped.",
+            default=True)
+
+    expand_snap_settings = bpy.props.BoolProperty(
+            name="Expand",
+            description="Expand, to display the settings",
+            default=False)
+
+    increments_grid = bpy.props.BoolProperty(
+            name="Increments of Grid",
+            description="Snap to increments of grid",
+            default=False)
+
+    category = bpy.props.StringProperty(
+            name="Category",
+            description="Choose a name for the category of the panel",
+            default="Snap Utilities",
+            update=update_panel)
+
+    incremental = bpy.props.FloatProperty(
+            name="Incremental",
+            description="Snap in defined increments",
+            default=0,
+            min=0,
+            step=1,
+            precision=3)
+
+    relative_scale = bpy.props.FloatProperty(
+            name="Relative Scale",
+            description="Value that divides the global scale.",
+            default=1,
+            min=0,
+            step=1,
+            precision=3)
+
+    out_color = bpy.props.FloatVectorProperty(name="OUT", default=(0.0, 0.0, 0.0, 0.5), size=4, subtype="COLOR", min=0, max=1)
+    face_color = bpy.props.FloatVectorProperty(name="FACE", default=(1.0, 0.8, 0.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
+    edge_color = bpy.props.FloatVectorProperty(name="EDGE", default=(0.0, 0.8, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
+    vert_color = bpy.props.FloatVectorProperty(name="VERT", default=(1.0, 0.5, 0.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
+    center_color = bpy.props.FloatVectorProperty(name="CENTER", default=(1.0, 0.0, 1.0, 1.0), size=4, subtype="COLOR", min=0, max=1)
+    perpendicular_color = bpy.props.FloatVectorProperty(name="PERPENDICULAR", default=(0.1, 0.5, 0.5, 1.0), size=4, subtype="COLOR", min=0, max=1)
+    constrain_shift_color = bpy.props.FloatVectorProperty(name="SHIFT CONSTRAIN", default=(0.8, 0.5, 0.4, 1.0), size=4, subtype="COLOR", min=0, max=1)
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.label(text="Snap Colors:")
+        split = layout.split()
+
+        col = split.column()
+        col.prop(self, "out_color")
+        col.prop(self, "constrain_shift_color")
+        col = split.column()
+        col.prop(self, "face_color")
+        col = split.column()
+        col.prop(self, "edge_color")
+        col = split.column()
+        col.prop(self, "vert_color")
+        col = split.column()
+        col.prop(self, "center_color")
+        col = split.column()
+        col.prop(self, "perpendicular_color")
+
+        row = layout.row()
+
+        col = row.column()
+        col.label(text="Category:")
+        col.prop(self, "category", text="")
+        #col.label(text="Snap Items:")
+        col.prop(self, "incremental")
+        col.prop(self, "increments_grid")
+        if self.increments_grid:
+            col.prop(self, "relative_scale")
+
+        col.prop(self, "outer_verts")
+        row.separator()
+
+        col = row.column()
+        col.label(text="Line Tool:")
+        col.prop(self, "intersect")
+        col.prop(self, "create_face")
+        col.prop(self, "create_new_obj")
+
+def register():
+    print('Addon', __name__, 'registered')
+    bpy.utils.register_class(SnapAddonPreferences)
+    bpy.utils.register_class(SnapUtilitiesLine)
+    update_panel(None, bpy.context)
+    #bpy.utils.register_class(PanelSnapUtilities)
+
+def unregister():
+    bpy.utils.unregister_class(PanelSnapUtilities)
+    bpy.utils.unregister_class(SnapUtilitiesLine)
+    bpy.utils.unregister_class(SnapAddonPreferences)
+
+if __name__ == "__main__":
+    __name__ = "mesh_snap_utilities_line"
+    register()