diff --git a/mesh_snap_utilities_line/common_classes.py b/mesh_snap_utilities_line/common_classes.py
index e61d9354de42b7d18e8eeb77381c4419e7b86e46..7be2fa97dfd520ccb0513aa8dcab1791c2cbf1f8 100644
--- a/mesh_snap_utilities_line/common_classes.py
+++ b/mesh_snap_utilities_line/common_classes.py
@@ -14,14 +14,18 @@
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # ##### END GPL LICENSE BLOCK #####
-
 import bpy
 
-from mathutils import Vector
+from mathutils import (
+    Vector,
+    Matrix,
+    )
+from mathutils.geometry import intersect_point_line
 from .drawing_utilities import SnapDrawn
 from .common_utilities import (
     convert_distance,
     get_units_info,
+    location_3d_to_region_2d,
     )
 
 
@@ -96,9 +100,6 @@ class SnapNavigation():
             return True
 
         if evkey in self._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')
             return True
 
@@ -187,8 +188,7 @@ class CharMap:
                         self.line_pos = (pos + 1) % (len(self.length_entered) + 1)
 
                     try:
-                        self.length_entered_value = bpy.utils.units.to_value(
-                                self.unit_system, 'LENGTH', self.length_entered)
+                        self.length_entered_value = bpy.utils.units.to_value(self.unit_system, 'LENGTH', self.length_entered)
                     except:  # ValueError:
                         self.length_entered_value = 0.0 #invalid
                         #self.report({'INFO'}, "Operation not supported yet")
@@ -214,6 +214,110 @@ class CharMap:
         self.line_pos = 0
 
 
+class Constrain:
+    def __init__(self, peferences, scene, obj):
+        self.last_type = None
+        self.last_vec = None
+        self.rotMat = None
+        self.preferences = peferences
+        trans_orient = scene.transform_orientation_slots[0]
+        self.orientation = [None, None]
+        if trans_orient.type == 'LOCAL':
+            self.orientation[0] = obj.matrix_world.to_3x3().transposed()
+            self.orientation[1] = Matrix.Identity(3)
+        else:
+            self.orientation[0] = Matrix.Identity(3)
+            self.orientation[1] = obj.matrix_world.to_3x3().transposed()
+
+        self.orientation_id = 0
+        self.center_2d = Vector((0.0, 0.0))
+        self.projected_vecs = Matrix(([0.0, 0.0], [0.0, 0.0], [0.0, 0.0]))
+
+    def _constrain_set(self, mcursor):
+        vec = (mcursor - self.center_2d)
+        vec.normalize()
+
+        dot_x = abs(vec.dot(self.projected_vecs[0]))
+        dot_y = abs(vec.dot(self.projected_vecs[1]))
+        dot_z = abs(vec.dot(self.projected_vecs[2]))
+
+        if dot_x > dot_y and dot_x > dot_z:
+            vec = self.orientation[self.orientation_id][0]
+            type = 'X'
+
+        elif dot_y > dot_x and dot_y > dot_z:
+            vec = self.orientation[self.orientation_id][1]
+            type = 'Y'
+
+        else: # dot_z > dot_y and dot_z > dot_x:
+            vec = self.orientation[self.orientation_id][2]
+            type = 'Z'
+
+        return vec, type
+
+    def modal(self, event, shift_callback):
+        type = event.type
+        if self.last_type == type:
+            self.orientation_id += 1
+
+        if type == 'X':
+            if self.orientation_id < 2:
+                self.last_vec = self.orientation[self.orientation_id][0]
+            else:
+                self.orientation_id = 0
+                self.last_vec = type = None
+        elif type == 'Y':
+            if self.orientation_id < 2:
+                self.last_vec = self.orientation[self.orientation_id][1]
+            else:
+                self.orientation_id = 0
+                self.last_vec = type = None
+        elif type == 'Z':
+            if self.orientation_id < 2:
+                self.last_vec = self.orientation[self.orientation_id][2]
+            else:
+                self.orientation_id = 0
+                self.last_vec = type = None
+        elif shift_callback and type in {'RIGHT_SHIFT', 'LEFT_SHIFT'}:
+            if self.orientation_id < 1:
+                type = 'shift'
+                self.last_vec = shift_callback()
+            else:
+                self.orientation_id = 0
+                self.last_vec = type = None
+        else:
+            return False
+
+        self.preferences.auto_constrain = False
+        self.last_type = type
+        return True
+
+    def toogle(self):
+        self.rotMat = None # update
+        if self.preferences.auto_constrain:
+            self.orientation_id = (self.orientation_id + 1) % 2
+            self.preferences.auto_constrain = self.orientation_id != 0
+        else:
+            self.preferences.auto_constrain = True
+
+    def update(self, region, rv3d, mcursor, location):
+        if rv3d.view_matrix != self.rotMat:
+            self.rotMat = rv3d.view_matrix.copy()
+
+            self.center_2d = location_3d_to_region_2d(region, rv3d, location)
+
+            orig = location_3d_to_region_2d(region, rv3d, Vector())
+            self.projected_vecs[0] = location_3d_to_region_2d(region, rv3d, self.orientation[self.orientation_id][0]) - orig
+            self.projected_vecs[1] = location_3d_to_region_2d(region, rv3d, self.orientation[self.orientation_id][1]) - orig
+            self.projected_vecs[2] = location_3d_to_region_2d(region, rv3d, self.orientation[self.orientation_id][2]) - orig
+
+            self.projected_vecs[0].normalize()
+            self.projected_vecs[1].normalize()
+            self.projected_vecs[2].normalize()
+
+        return self._constrain_set(mcursor)
+
+
 class SnapUtilities:
     """
     __slots__ = (
@@ -304,7 +408,7 @@ class SnapUtilities:
             if obj.instance_type == 'COLLECTION':
                 mat = obj.matrix_world.copy()
                 for ob in obj.instance_collection.objects:
-                    snap_obj = self.sctx.add_obj(obj, mat @ ob.matrix_world)
+                    snap_obj = self.sctx.add_obj(ob, mat @ ob.matrix_world)
                     if is_moving:
                         moving_snp_objects.add(snap_obj)
 
@@ -332,7 +436,7 @@ class SnapUtilities:
             self.sctx.add_obj(obj, matrix)
 
 
-    def snap_context_init(self, context, snap_edge_and_vert = True):
+    def snap_context_init(self, context, snap_edge_and_vert=True):
         from .snap_context_l import global_snap_context_get
 
         #Create Snap Context
@@ -352,8 +456,6 @@ class SnapUtilities:
             self.draw_cache = widget.draw_cache
             if hasattr(widget, "normal"):
                 self.normal = widget.normal
-                #loc = widget.location
-                #self.vector_constrain = [loc, loc + widget.normal, self.constrain]
 
         else:
             #init these variables to avoid errors
@@ -382,11 +484,9 @@ class SnapUtilities:
         self.snap_vert = self.snap_edge = snap_edge_and_vert
 
         shading = context.space_data.shading
-        self.snap_face = not (snap_edge_and_vert and
-                             (shading.show_xray or shading.type == 'WIREFRAME'))
+        self.snap_face = not (snap_edge_and_vert and (shading.show_xray or shading.type == 'WIREFRAME'))
 
-        self.sctx.set_snap_mode(
-                 self.snap_vert, self.snap_edge, self.snap_face)
+        self.sctx.set_snap_mode(self.snap_vert, self.snap_edge, self.snap_face)
 
         #Configure the unit of measure
         unit_system = context.scene.unit_settings.system
@@ -394,8 +494,7 @@ class SnapUtilities:
         scale /= context.space_data.overlay.grid_scale
         self.rd = bpy.utils.units.to_value(unit_system, 'LENGTH', str(1 / scale))
 
-        self.incremental = bpy.utils.units.to_value(
-                unit_system, 'LENGTH', str(self.preferences.incremental))
+        self.incremental = bpy.utils.units.to_value(unit_system, 'LENGTH', str(self.preferences.incremental))
 
     def snap_to_grid(self):
         if self.type == 'OUT' and self.preferences.increments_grid:
diff --git a/mesh_snap_utilities_line/op_line.py b/mesh_snap_utilities_line/op_line.py
index 7af0c684050ed176b671e67b011dd87cd93c2100..20d5f0b82ee96352df162fdb3c0f838544a20e53 100644
--- a/mesh_snap_utilities_line/op_line.py
+++ b/mesh_snap_utilities_line/op_line.py
@@ -25,6 +25,7 @@ from .drawing_utilities import SnapDrawn
 from .common_utilities import snap_utilities
 from .common_classes import (
     CharMap,
+    Constrain,
     SnapNavigation,
     SnapUtilities,
     )
@@ -206,7 +207,7 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
     bl_label = "Line Tool"
     bl_options = {'REGISTER'}
 
-    wait_for_input: bpy.props.BoolProperty(name="Wait for Input", default=True)
+    wait_for_input : bpy.props.BoolProperty(name="Wait for Input", default=True)
 
     def _exit(self, context):
         #avoids unpredictable crashs
@@ -229,7 +230,7 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
         self.list_verts = []
         self.list_edges = []
         self.list_verts_co = []
-        self.bool_update = False
+        self.bool_update = True
         self.vector_constrain = ()
         self.len = 0
 
@@ -240,6 +241,12 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
         self.main_snap_obj = self.snap_obj = self.sctx._get_snap_obj_by_obj(self.obj)
         self.main_bm = self.bm
 
+    def _shift_contrain_callback(self):
+        if isinstance(self.geom, bmesh.types.BMEdge):
+            mat = self.main_snap_obj.mat
+            verts_co = [mat @ v.co for v in self.geom.verts]
+            return verts_co[1] - verts_co[0]
+
     def modal(self, context, event):
         if self.navigation_ops.run(context, event, self.prevloc if self.vector_constrain else self.location):
             return {'RUNNING_MODAL'}
@@ -270,6 +277,8 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
         is_making_lines = bool(self.list_verts_co)
 
         if (event.type == 'MOUSEMOVE' or self.bool_update) and self.charmap.length_entered_value == 0.0:
+            mval = Vector((event.mouse_region_x, event.mouse_region_y))
+
             if self.rv3d.view_matrix != self.rotMat:
                 self.rotMat = self.rv3d.view_matrix.copy()
                 self.bool_update = True
@@ -277,8 +286,6 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
             else:
                 self.bool_update = False
 
-            mval = Vector((event.mouse_region_x, event.mouse_region_y))
-
             self.snap_obj, self.prevloc, self.location, self.type, self.bm, self.geom, self.len = snap_utilities(
                     self.sctx,
                     self.main_snap_obj,
@@ -289,25 +296,10 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
 
             self.snap_to_grid()
 
-            if is_making_lines and self.keyf8:
-                lloc = self.list_verts_co[-1]
-                view_vec, orig = self.sctx.last_ray
-                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 and ax > az
-                vec.y = ay > ax and ay > az
-                vec.z = az > ay and az > ax
-                if not (vec.x or vec.y or vec.z):
-                    self.vector_constrain = None
-                else:
-                    vc = lloc + vec
-                    if self.vector_constrain is None or 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]
-                        #del vc, type
-
-                #del view_vec, orig, location, ax, ay, az, vec
+            if is_making_lines and self.preferences.auto_constrain:
+                loc = self.list_verts_co[-1]
+                vec, type = self.constrain.update(self.sctx.region, self.sctx.rv3d, mval, loc)
+                self.vector_constrain = [loc, loc + vec, type]
 
         if event.value == 'PRESS':
             if is_making_lines and self.charmap.modal_(context, event):
@@ -319,29 +311,17 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
                     self.location = self.list_verts_co[-1] + (vector * text_value)
                     del vector
 
-            elif event.type in self.constrain_keys:
+            elif self.constrain.modal(event, self._shift_contrain_callback):
                 self.bool_update = True
-                self.keyf8 = False
-
-                if self.vector_constrain and self.vector_constrain[2] == event.type:
-                    self.vector_constrain = ()
+                if self.constrain.last_vec:
+                    if self.list_verts_co:
+                        loc = self.list_verts_co[-1]
+                    else:
+                        loc = self.location
 
+                    self.vector_constrain = (loc, loc + self.constrain.last_vec, self.constrain.last_type)
                 else:
-                    if event.shift:
-                        if isinstance(self.geom, bmesh.types.BMEdge):
-                            mat = self.main_snap_obj.mat
-                            verts_co = [mat @ v.co for v in self.geom.verts]
-                            if is_making_lines:
-                                loc = self.list_verts_co[-1]
-                                self.vector_constrain = (loc, loc + verts_co[1] - verts_co[0], event.type)
-                            else:
-                                self.vector_constrain = verts_co + [event.type]
-                    else:
-                        if is_making_lines:
-                            loc = self.list_verts_co[-1]
-                        else:
-                            loc = self.location
-                        self.vector_constrain = [loc, loc + self.constrain_keys[event.type], event.type]
+                    self.vector_constrain = None
 
             elif event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER'}:
                 if event.type == 'LEFTMOUSE' or self.charmap.length_entered_value != 0.0:
@@ -351,7 +331,6 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
 
                     mat_inv = self.main_snap_obj.mat.inverted_safe()
                     point = mat_inv @ self.location
-                    # with constraint the intersection can be in a different element of the selected one
                     geom2 = self.geom
                     if geom2:
                         geom2.select = False
@@ -369,7 +348,7 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
 
             elif event.type == 'F8':
                 self.vector_constrain = None
-                self.keyf8 = self.keyf8 is False
+                self.constrain.toogle()
 
             elif event.type in {'RIGHTMOUSE', 'ESC'}:
                 if not self.wait_for_input or not is_making_lines or event.type == 'ESC':
@@ -406,6 +385,8 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
             self.snap_context_init(context)
             self.snap_context_update(context)
 
+            self.constrain = Constrain(self.preferences, context.scene, self.obj)
+
             self.intersect = self.preferences.intersect
             self.create_face = self.preferences.create_face
             self.navigation_ops = SnapNavigation(context, True)
@@ -427,10 +408,8 @@ class SnapUtilitiesLine(SnapUtilities, bpy.types.Operator):
             #Store values from 3d view context
             self.rv3d = context.region_data
             self.rotMat = self.rv3d.view_matrix.copy()
-            # self.obj_glmatrix = bgl.Buffer(bgl.GL_FLOAT, [4, 4], self.obj_matrix.transposed())
-
-            #Init event variables
-            self.keyf8 = False
+            # self.obj_glmatrix = bgl.Buffer(bgl.GL_FLOAT, [4, 4],
+            # self.obj_matrix.transposed())
 
             #modals
             context.window_manager.modal_handler_add(self)