code cleanup: use const events for modal and invoke operators.
[blender.git] / source / blender / editors / mesh / editmesh_knife.c
index 6bbcd1d253e2e8e283a94bcf41e7d2c5b7fd53cb..af793c1e0a2fbf1225ea8b73615cf58995915efc 100644 (file)
  *  \ingroup edmesh
  */
 
-#define _USE_MATH_DEFINES
+#ifdef _MSC_VER
+#  define _USE_MATH_DEFINES
+#endif
 
 #include "MEM_guardedalloc.h"
 
 #include "BLI_blenlib.h"
 #include "BLI_array.h"
 #include "BLI_math.h"
-#include "BLI_rand.h"
 #include "BLI_smallhash.h"
-#include "BLI_scanfill.h"
 #include "BLI_memarena.h"
 
+#include "BLF_translation.h"
+
 #include "BKE_DerivedMesh.h"
 #include "BKE_context.h"
-#include "BKE_depsgraph.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h" /* for paint cursor */
@@ -57,7 +58,6 @@
 #include "WM_types.h"
 
 #include "DNA_scene_types.h"
-#include "DNA_mesh_types.h"
 #include "DNA_object_types.h"
 #include "BKE_tessmesh.h"
 #include "UI_resources.h"
@@ -71,6 +71,9 @@
 
 #define KMAXDIST    10  /* max mouse distance from edge before not detecting it */
 
+#define KNIFE_FLT_EPS          0.00001f
+#define KNIFE_FLT_EPS_SQUARED  (KNIFE_FLT_EPS * KNIFE_FLT_EPS)
+
 typedef struct KnifeColors {
        unsigned char line[3];
        unsigned char edge[3];
@@ -84,6 +87,7 @@ typedef struct KnifeColors {
 typedef struct KnifeVert {
        BMVert *v; /* non-NULL if this is an original vert */
        ListBase edges;
+       ListBase faces;
 
        float co[3], cageco[3], sco[3]; /* sco is screen coordinates for cageco */
        short flag, draw, isface, inspace;
@@ -100,7 +104,7 @@ typedef struct KnifeEdge {
        ListBase faces;
        int draw;
 
-       BMEdge *e, *oe; /* non-NULL if this is an original edge */
+       BMEdge *e /* , *e_old */; /* non-NULL if this is an original edge */
 } KnifeEdge;
 
 typedef struct BMEdgeHit {
@@ -126,7 +130,7 @@ typedef struct KnifePosData {
        BMFace *bmface;
        int is_space;
 
-       int mval[2]; /* mouse screen position */
+       float mval[2]; /* mouse screen position (may be non-integral if snapped to something) */
 } KnifePosData;
 
 /* struct for properties used while drawing */
@@ -159,7 +163,7 @@ typedef struct KnifeTool_OpData {
        int totlinehit;
 
        /* Data for mouse-position-derived data (cur) and previous click (prev) */
-       KnifePosData cur, prev;
+       KnifePosData curr, prev;
 
        int totkedge, totkvert;
 
@@ -175,6 +179,7 @@ typedef struct KnifeTool_OpData {
        char select_result;  /* set on initialization */
 
        short is_ortho;
+       float ortho_extent;
        float clipsta, clipend;
 
        enum {
@@ -200,28 +205,37 @@ typedef struct KnifeTool_OpData {
 
 static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f);
 
-static void knife_input_ray_cast(KnifeTool_OpData *kcd, const int mval_i[2],
+#if 0
+static void knife_input_ray_cast(KnifeTool_OpData *kcd, const float mval[2],
                                  float r_origin[3], float r_ray[3]);
+#endif
+static void knife_input_ray_segment(KnifeTool_OpData *kcd, const float mval[2], const float ofs,
+                                    float r_origin[3], float r_dest[3]);
 
 static void knife_update_header(bContext *C, KnifeTool_OpData *kcd)
 {
-       #define HEADER_LENGTH 190
+       #define HEADER_LENGTH 256
        char header[HEADER_LENGTH];
 
-       BLI_snprintf(header, HEADER_LENGTH, "LMB: define cut lines, Return/Spacebar: confirm, Esc or RMB: cancel, E: new cut, Ctrl: midpoint snap (%s), "
-                    "Shift: ignore snap (%s), C: angle constrain (%s), Z: cut through (%s)",
-                    kcd->snap_midpoints ? "On" : "Off",
-                    kcd->ignore_edge_snapping ?  "On" : "Off",
-                    kcd->angle_snapping ? "On" : "Off",
-                    kcd->cut_through ? "On" : "Off");
+       BLI_snprintf(header, HEADER_LENGTH, IFACE_("LMB: define cut lines, Return/Spacebar: confirm, Esc or RMB: cancel, "
+                                                  "E: new cut, Ctrl: midpoint snap (%s), Shift: ignore snap (%s), "
+                                                  "C: angle constrain (%s), Z: cut through (%s)"),
+                    kcd->snap_midpoints ? IFACE_("On") : IFACE_("Off"),
+                    kcd->ignore_edge_snapping ?  IFACE_("On") : IFACE_("Off"),
+                    kcd->angle_snapping ? IFACE_("On") : IFACE_("Off"),
+                    kcd->cut_through ? IFACE_("On") : IFACE_("Off"));
 
        ED_area_headerprint(CTX_wm_area(C), header);
 }
 
+BLI_INLINE int round_ftoi(float x)
+{
+       return x > 0.0f ?  (int)(x + 0.5f) : (int)(x - 0.5f);
+}
 
 static void knife_project_v3(KnifeTool_OpData *kcd, const float co[3], float sco[3])
 {
-       ED_view3d_project_float_v3(kcd->ar, co, sco, kcd->projmat);
+       ED_view3d_project_float_v3_m4(kcd->ar, co, sco, kcd->projmat);
 }
 
 static void knife_pos_data_clear(KnifePosData *kpd)
@@ -231,8 +245,8 @@ static void knife_pos_data_clear(KnifePosData *kpd)
        kpd->vert = NULL;
        kpd->edge = NULL;
        kpd->bmface = NULL;
-       kpd->mval[0] = 0;
-       kpd->mval[1] = 0;
+       kpd->mval[0] = 0.0f;
+       kpd->mval[1] = 0.0f;
 }
 
 static ListBase *knife_empty_list(KnifeTool_OpData *kcd)
@@ -277,6 +291,32 @@ static void knife_add_to_vert_edges(KnifeTool_OpData *kcd, KnifeEdge *kfe)
        knife_append_list(kcd, &kfe->v2->edges, kfe);
 }
 
+/* Add faces of an edge to a KnifeVert's faces list.  No checks for dups. */
+static void knife_add_edge_faces_to_vert(KnifeTool_OpData *kcd, KnifeVert *kfv, BMEdge *e)
+{
+       BMIter bmiter;
+       BMFace *f;
+
+       BM_ITER_ELEM(f, &bmiter, e, BM_FACES_OF_EDGE) {
+               knife_append_list(kcd, &kfv->faces, f);
+       }
+}
+
+/* Find a face in common in the two faces lists.
+ * If more than one, return the first; if none, return NULL */
+static BMFace *knife_find_common_face(ListBase *faces1, ListBase *faces2)
+{
+       Ref *ref1, *ref2;
+
+       for (ref1 = faces1->first; ref1; ref1 = ref1->next) {
+               for (ref2 = faces2->first; ref2; ref2 = ref2->next) {
+                       if (ref1->ref == ref2->ref)
+                               return (BMFace *)(ref1->ref);
+               }
+       }
+       return NULL;
+}
+
 static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], float *cageco)
 {
        KnifeVert *kfv = BLI_mempool_calloc(kcd->kverts);
@@ -298,22 +338,27 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v)
        KnifeVert *kfv = BLI_ghash_lookup(kcd->origvertmap, v);
 
        if (!kfv) {
+               BMIter bmiter;
+               BMFace *f;
+
                kfv = new_knife_vert(kcd, v->co, kcd->cagecos[BM_elem_index_get(v)]);
                kfv->v = v;
                BLI_ghash_insert(kcd->origvertmap, v, kfv);
+               BM_ITER_ELEM(f, &bmiter, v, BM_FACES_OF_VERT) {
+                       knife_append_list(kcd, &kfv->faces, f);
+               }
        }
 
        return kfv;
 }
 
-/**
- * get a KnifeEdge wrapper for an existing BMEdge
- * \note #knife_get_face_kedges / #get_bm_knife_edge are called recursively - KEEP STACK MEM USAGE LOW */
+/* get a KnifeEdge wrapper for an existing BMEdge */
 static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e)
 {
        KnifeEdge *kfe = BLI_ghash_lookup(kcd->origedgemap, e);
        if (!kfe) {
-               BMLoop *l_iter, *l_first;
+               BMIter bmiter;
+               BMFace *f;
 
                kfe = new_knife_edge(kcd);
                kfe->e = e;
@@ -324,17 +369,9 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e)
 
                BLI_ghash_insert(kcd->origedgemap, e, kfe);
 
-               /* avoid BM_ITER because of stack memory usage
-                * otherwise we could use BM_FACES_OF_EDGE */
-               l_iter = l_first = e->l;
-               do {
-                       knife_append_list(kcd, &kfe->faces, l_iter->f);
-
-                       /* ensures the kedges lst for this f is initialized,
-                        * it automatically adds kfe by itself */
-                       knife_get_face_kedges(kcd, l_iter->f);
-
-               } while ((l_iter = l_iter->radial_next) != l_first);
+               BM_ITER_ELEM(f, &bmiter, e, BM_FACES_OF_EDGE) {
+                       knife_append_list(kcd, &kfe->faces, f);
+               }
        }
 
        return kfe;
@@ -344,43 +381,38 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e)
  * Copy the current position data into prev. */
 static void knife_start_cut(KnifeTool_OpData *kcd)
 {
-       kcd->prev = kcd->cur;
-       kcd->cur.is_space = 0; /*TODO: why do we do this? */
+       kcd->prev = kcd->curr;
+       kcd->curr.is_space = 0; /*TODO: why do we do this? */
 
        if (kcd->prev.vert == NULL && kcd->prev.edge == NULL && is_zero_v3(kcd->prev.cage)) {
                /* Make prevcage a point on the view ray to mouse closest to a point on model: choose vertex 0 */
-               float origin[3], ray[3], co[3];
+               float origin[3], origin_ofs[3];
                BMVert *v0;
 
-               knife_input_ray_cast(kcd, kcd->cur.mval, origin, ray);
-               add_v3_v3v3(co, origin, ray);
+               knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
                v0 = BM_vert_at_index(kcd->em->bm, 0);
                if (v0) {
-                       closest_to_line_v3(kcd->prev.cage, v0->co, co, origin);
+                       closest_to_line_v3(kcd->prev.cage, v0->co, origin_ofs, origin);
                        copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */
-                       copy_v3_v3(kcd->cur.cage, kcd->prev.cage);
-                       copy_v3_v3(kcd->cur.co, kcd->prev.co);
+                       copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
+                       copy_v3_v3(kcd->curr.co, kcd->prev.co);
                }
        }
 }
 
-/**
- * \note #knife_get_face_kedges / #get_bm_knife_edge are called recursively - KEEP STACK MEM USAGE LOW */
 static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f)
 {
        ListBase *lst = BLI_ghash_lookup(kcd->kedgefacemap, f);
 
        if (!lst) {
-               BMLoop *l_iter, *l_first;
+               BMIter bmiter;
+               BMEdge *e;
 
                lst = knife_empty_list(kcd);
 
-               /* avoid BM_ITER because of stack memory usage
-                * otherwise we could use BM_EDGES_OF_FACE */
-               l_iter = l_first = BM_FACE_FIRST_LOOP(f);
-               do {
-                       knife_append_list(kcd, lst, get_bm_knife_edge(kcd, l_iter->e));
-               } while ((l_iter = l_iter->next) != l_first);
+               BM_ITER_ELEM(e, &bmiter, f, BM_EDGES_OF_FACE) {
+                       knife_append_list(kcd, lst, get_bm_knife_edge(kcd, e));
+               }
 
                BLI_ghash_insert(kcd->kedgefacemap, f, lst);
        }
@@ -389,35 +421,9 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f)
 }
 
 /* finds the proper face to restrict face fill to */
-static void knife_find_basef(KnifeTool_OpData *kcd, KnifeEdge *kfe)
+static void knife_find_basef(KnifeEdge *kfe)
 {
-       if (!kfe->basef) {
-               Ref *r1, *r2, *r3, *r4;
-
-               if (kfe->v1->isface || kfe->v2->isface) {
-                       if (kfe->v2->isface)
-                               kfe->basef = kcd->cur.bmface;
-                       else
-                               kfe->basef = kcd->prev.bmface;
-               }
-               else {
-                       for (r1 = kfe->v1->edges.first; r1 && !kfe->basef; r1 = r1->next) {
-                               KnifeEdge *ke1 = r1->ref;
-                               for (r2 = ke1->faces.first; r2 && !kfe->basef; r2 = r2->next) {
-                                       for (r3 = kfe->v2->edges.first; r3 && !kfe->basef; r3 = r3->next) {
-                                               KnifeEdge *ke2 = r3->ref;
-
-                                               for (r4 = ke2->faces.first; r4 && !kfe->basef; r4 = r4->next) {
-                                                       if (r2->ref == r4->ref) {
-                                                               kfe->basef = r2->ref;
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-               }
-               /* ok, at this point kfe->basef should be set if any valid possibility exists */
-       }
+       kfe->basef = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces);
 }
 
 static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f)
@@ -430,10 +436,11 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float
 {
        KnifeEdge *newkfe = new_knife_edge(kcd);
        Ref *ref;
+       BMFace *f;
        float perc, cageco[3], l12;
 
        l12 = len_v3v3(kfe->v1->co, kfe->v2->co);
-       if (l12 < FLT_EPSILON * 80) {
+       if (l12 < KNIFE_FLT_EPS) {
                copy_v3_v3(cageco, kfe->v1->cageco);
        }
        else {
@@ -444,6 +451,17 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float
        newkfe->v1 = kfe->v1;
        newkfe->v2 = new_knife_vert(kcd, co, cageco);
        newkfe->v2->draw = 1;
+       if (kfe->e) {
+               knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e);
+       }
+       else {
+               /* kfe cuts across an existing face.
+                * If v1 and v2 are in multiple faces together (e.g., if they
+                * are in doubled polys) then this arbitrarily chooses one of them */
+               f = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces);
+               if (f)
+                       knife_append_list(kcd, &newkfe->v2->faces, f);
+       }
        newkfe->basef = kfe->basef;
 
        ref = find_ref(&kfe->v1->edges, kfe);
@@ -465,15 +483,15 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float
        return newkfe->v2;
 }
 
-/* Make a single KnifeEdge for cut from kcd->prev to kcd->cur.
+/* Make a single KnifeEdge for cut from kcd->prev to kcd->curr.
  * and move cur data to prev. */
 static void knife_add_single_cut(KnifeTool_OpData *kcd)
 {
        KnifeEdge *kfe = new_knife_edge(kcd), *kfe2 = NULL, *kfe3 = NULL;
 
-       if (kcd->prev.vert && kcd->prev.vert == kcd->cur.vert)
+       if (kcd->prev.vert && kcd->prev.vert == kcd->curr.vert)
                return;
-       if (kcd->prev.edge && kcd->prev.edge == kcd->cur.edge)
+       if (kcd->prev.edge && kcd->prev.edge == kcd->curr.edge)
                return;
 
        kfe->draw = 1;
@@ -490,28 +508,32 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd)
                kfe->v1->inspace = kcd->prev.is_space;
                kfe->draw = !kcd->prev.is_space;
                kfe->v1->isface = 1;
+               if (kfe->v1->draw && kcd->prev.bmface)
+                       knife_append_list(kcd, &kfe->v1->faces, kcd->prev.bmface);
        }
 
-       if (kcd->cur.vert) {
-               kfe->v2 = kcd->cur.vert;
+       if (kcd->curr.vert) {
+               kfe->v2 = kcd->curr.vert;
        }
-       else if (kcd->cur.edge) {
-               kfe->v2 = knife_split_edge(kcd, kcd->cur.edge, kcd->cur.co, &kfe3);
-               kcd->cur.vert = kfe->v2;
+       else if (kcd->curr.edge) {
+               kfe->v2 = knife_split_edge(kcd, kcd->curr.edge, kcd->curr.co, &kfe3);
+               kcd->curr.vert = kfe->v2;
        }
        else {
-               kfe->v2 = new_knife_vert(kcd, kcd->cur.co, kcd->cur.co);
-               kfe->v2->draw = !kcd->cur.is_space;
+               kfe->v2 = new_knife_vert(kcd, kcd->curr.co, kcd->curr.co);
+               kfe->v2->draw = !kcd->curr.is_space;
                kfe->v2->isface = 1;
-               kfe->v2->inspace = kcd->cur.is_space;
+               kfe->v2->inspace = kcd->curr.is_space;
+               if (kfe->v2->draw && kcd->curr.bmface)
+                       knife_append_list(kcd, &kfe->v2->faces, kcd->curr.bmface);
 
-               if (kcd->cur.is_space)
+               if (kcd->curr.is_space)
                        kfe->draw = 0;
 
-               kcd->cur.vert = kfe->v2;
+               kcd->curr.vert = kfe->v2;
        }
 
-       knife_find_basef(kcd, kfe);
+       knife_find_basef(kfe);
 
        knife_add_to_vert_edges(kcd, kfe);
 
@@ -519,12 +541,12 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd)
                knife_edge_append_face(kcd, kfe, kfe->basef);
 
        /* sanity check to make sure we're in the right edge/face lists */
-       if (kcd->cur.bmface) {
-               if (!find_ref(&kfe->faces, kcd->cur.bmface)) {
-                       knife_edge_append_face(kcd, kfe, kcd->cur.bmface);
+       if (kcd->curr.bmface) {
+               if (!find_ref(&kfe->faces, kcd->curr.bmface)) {
+                       knife_edge_append_face(kcd, kfe, kcd->curr.bmface);
                }
 
-               if (kcd->prev.bmface && kcd->prev.bmface != kcd->cur.bmface) {
+               if (kcd->prev.bmface && kcd->prev.bmface != kcd->curr.bmface) {
                        if (!find_ref(&kfe->faces, kcd->prev.bmface)) {
                                knife_edge_append_face(kcd, kfe, kcd->prev.bmface);
                        }
@@ -532,7 +554,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd)
        }
 
        /* set up for next cut */
-       kcd->prev = kcd->cur;
+       kcd->prev = kcd->curr;
 }
 
 static int verge_linehit(const void *vlh1, const void *vlh2)
@@ -544,6 +566,67 @@ static int verge_linehit(const void *vlh1, const void *vlh2)
        else return 0;
 }
 
+/* If there's a linehit connected (same face) as testi in range [firsti, lasti], return the first such, else -1.
+ * If testi is out of range, look for connection to f instead, if f is non-NULL */
+static int find_connected_linehit(KnifeTool_OpData *kcd, int testi, BMFace *f, int firsti, int lasti)
+{
+       int i;
+
+       for (i = firsti; i <= lasti; i++) {
+               if (testi >= 0 && testi < kcd->totlinehit) {
+                       if (knife_find_common_face(&kcd->linehits[testi].kfe->faces,
+                                                  &kcd->linehits[i].kfe->faces))
+                       {
+                               return i;
+                       }
+               }
+               else if (f) {
+                       if (find_ref(&kcd->linehits[i].kfe->faces, f))
+                               return i;
+               }
+       }
+       return -1;
+}
+
+/* Sort in order of distance along cut line, but take care when distances are equal */
+static void knife_sort_linehits(KnifeTool_OpData *kcd)
+{
+       int i, j, k, nexti, nsame;
+
+       qsort(kcd->linehits, kcd->totlinehit, sizeof(BMEdgeHit), verge_linehit);
+
+       /* for ranges of equal "l", swap if neccesary to make predecessor and
+        * successor faces connected to the linehits at either end of the range */
+       for (i = 0; i < kcd->totlinehit - 1; i = nexti) {
+               for (j = i + 1; j < kcd->totlinehit; j++) {
+                       if (fabsf(kcd->linehits[j].l - kcd->linehits[i].l) > KNIFE_FLT_EPS)
+                               break;
+               }
+               nexti = j;
+               j--;
+               nsame = j - i;
+               if (nsame > 0) {
+                       /* find something connected to predecessor of equal range */
+                       k = find_connected_linehit(kcd, i - 1, kcd->prev.bmface, i, j);
+                       if (k != -1) {
+                               if (k != i) {
+                                       SWAP(BMEdgeHit, kcd->linehits[i], kcd->linehits[k]);
+                               }
+                               i++;
+                               nsame--;
+                       }
+                       if (nsame > 0) {
+                               /* find something connected to successor of equal range */
+                               k = find_connected_linehit(kcd, j + 1, kcd->curr.bmface, i, j);
+                               if (k != -1 && k != j) {
+                                       SWAP(BMEdgeHit, kcd->linehits[j], kcd->linehits[k]);
+                               }
+                       }
+                       /* rest of same range doesn't matter because we won't connect them */
+               }
+       }
+}
+
 static void knife_add_single_cut_through(KnifeTool_OpData *kcd, KnifeVert *v1, KnifeVert *v2, BMFace *f)
 {
        KnifeEdge *kfenew;
@@ -565,6 +648,7 @@ static void knife_get_vert_faces(KnifeTool_OpData *kcd, KnifeVert *kfv, BMFace *
 {
        BMIter bmiter;
        BMFace *f;
+       Ref *r;
 
        if (kfv->isface && facef) {
                knife_append_list(kcd, lst, facef);
@@ -574,6 +658,11 @@ static void knife_get_vert_faces(KnifeTool_OpData *kcd, KnifeVert *kfv, BMFace *
                        knife_append_list(kcd, lst, f);
                }
        }
+       else {
+               for (r = kfv->faces.first; r; r = r->next) {
+                       knife_append_list(kcd, lst, r->ref);
+               }
+       }
 }
 
 static void knife_get_edge_faces(KnifeTool_OpData *kcd, KnifeEdge *kfe, ListBase *lst)
@@ -608,29 +697,30 @@ static void knife_cut_through(KnifeTool_OpData *kcd)
                return;
        }
 
+       /* TODO: probably don't need to sort at all */
        qsort(kcd->linehits, kcd->totlinehit, sizeof(BMEdgeHit), verge_linehit);
        splitkfe = MEM_callocN(kcd->totlinehit * sizeof(KnifeEdge *), "knife_cut_through");
 
        if (kcd->prev.vert) {
-               if (kcd->prev.vert == kcd->cur.vert)
+               if (kcd->prev.vert == kcd->curr.vert)
                        return;
                firstv = kcd->prev.vert;
                knife_get_vert_faces(kcd, firstv, kcd->prev.bmface, &firstfaces);
        }
        else if (kcd->prev.edge) {
-               if (kcd->prev.edge == kcd->cur.edge)
+               if (kcd->prev.edge == kcd->curr.edge)
                        return;
                firstv = knife_split_edge(kcd, kcd->prev.edge, kcd->prev.co, &kfe3);
                knife_get_edge_faces(kcd, kcd->prev.edge, &firstfaces);
        }
 
-       if (kcd->cur.vert) {
-               lastv = kcd->cur.vert;
-               knife_get_vert_faces(kcd, lastv, kcd->cur.bmface, &lastfaces);
+       if (kcd->curr.vert) {
+               lastv = kcd->curr.vert;
+               knife_get_vert_faces(kcd, lastv, kcd->curr.bmface, &lastfaces);
        }
-       else if (kcd->cur.edge) {
-               lastv = knife_split_edge(kcd, kcd->cur.edge, kcd->cur.co, &kfe3);
-               knife_get_edge_faces(kcd, kcd->cur.edge, &lastfaces);
+       else if (kcd->curr.edge) {
+               lastv = knife_split_edge(kcd, kcd->curr.edge, kcd->curr.co, &kfe3);
+               knife_get_edge_faces(kcd, kcd->curr.edge, &lastfaces);
        }
 
        if (firstv) {
@@ -639,7 +729,7 @@ static void knife_cut_through(KnifeTool_OpData *kcd)
                for (r = firstfaces.first; r; r = r->next) {
                        f = r->ref;
                        found = 0;
-                       for (j = 0, lh2 = kcd->linehits; j < kcd->totlinehit; j++, lh2++) {
+                       for (j = 0, lh2 = kcd->linehits; j < kcd->totlinehit && !found; j++, lh2++) {
                                kfe2 = lh2->kfe;
                                for (r2 = kfe2->faces.first; r2; r2 = r2->next) {
                                        if (r2->ref == f) {
@@ -669,7 +759,7 @@ static void knife_cut_through(KnifeTool_OpData *kcd)
                for (r = kfe->faces.first; r; r = r->next) {
                        f = r->ref;
                        found = 0;
-                       for (j = i + 1, lh2 = lh + 1; j < kcd->totlinehit; j++, lh2++) {
+                       for (j = i + 1, lh2 = lh + 1; j < kcd->totlinehit && !found; j++, lh2++) {
                                kfe2 = lh2->kfe;
                                for (r2 = kfe2->faces.first; r2; r2 = r2->next) {
                                        if (r2->ref == f) {
@@ -699,15 +789,16 @@ static void knife_cut_through(KnifeTool_OpData *kcd)
        kcd->totlinehit = 0;
 
        /* set up for next cut */
-       kcd->prev = kcd->cur;
+       kcd->curr.vert = lastv;
+       kcd->prev = kcd->curr;
 }
 
 /* User has just left-clicked after the first time.
- * Add all knife cuts implied by line from prev to cur.
+ * Add all knife cuts implied by line from prev to curr.
  * If that line crossed edges then kcd->linehits will be non-NULL. */
 static void knife_add_cut(KnifeTool_OpData *kcd)
 {
-       KnifePosData savcur = kcd->cur;
+       KnifePosData savcur = kcd->curr;
 
        if (kcd->cut_through) {
                knife_cut_through(kcd);
@@ -716,14 +807,14 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
                BMEdgeHit *lh, *lastlh, *firstlh;
                int i;
 
-               qsort(kcd->linehits, kcd->totlinehit, sizeof(BMEdgeHit), verge_linehit);
+               knife_sort_linehits(kcd);
 
                lh = kcd->linehits;
                lastlh = firstlh = NULL;
                for (i = 0; i < kcd->totlinehit; i++, (lastlh = lh), lh++) {
                        BMFace *f = lastlh ? lastlh->f : lh->f;
 
-                       if (lastlh && len_v3v3(lastlh->hit, lh->hit) == 0.0f) {
+                       if (lastlh && len_squared_v3v3(lastlh->hit, lh->hit) == 0.0f) {
                                if (!firstlh)
                                        firstlh = lastlh;
                                continue;
@@ -742,9 +833,13 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
                                lastlh = firstlh = NULL;
                        }
 
-                       if (len_v3v3(kcd->prev.cage, lh->realhit) < FLT_EPSILON * 80)
+                       if (len_squared_v3v3(kcd->prev.cage, lh->realhit) < KNIFE_FLT_EPS_SQUARED)
                                continue;
-                       if (len_v3v3(kcd->cur.cage, lh->realhit) < FLT_EPSILON * 80)
+                       if (len_squared_v3v3(kcd->curr.cage, lh->realhit) < KNIFE_FLT_EPS_SQUARED)
+                               continue;
+
+                       /* first linehit may be down face parallel to view */
+                       if (!lastlh && fabsf(lh->l) < KNIFE_FLT_EPS)
                                continue;
 
                        if (kcd->prev.is_space) {
@@ -757,12 +852,18 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
                                continue;
                        }
 
-                       kcd->cur.is_space = 0;
-                       kcd->cur.edge = lh->kfe;
-                       kcd->cur.bmface = lh->f;
-                       kcd->cur.vert = lh->v;
-                       copy_v3_v3(kcd->cur.co, lh->hit);
-                       copy_v3_v3(kcd->cur.cage, lh->cagehit);
+                       kcd->curr.is_space = 0;
+                       kcd->curr.edge = lh->kfe;
+                       kcd->curr.bmface = lh->f;
+                       kcd->curr.vert = lh->v;
+                       copy_v3_v3(kcd->curr.co, lh->hit);
+                       copy_v3_v3(kcd->curr.cage, lh->cagehit);
+
+                       /* don't draw edges down faces parallel to view */
+                       if (lastlh && fabsf(lastlh->l - lh->l) < KNIFE_FLT_EPS) {
+                               kcd->prev = kcd->curr;
+                               continue;
+                       }
 
                        knife_add_single_cut(kcd);
                }
@@ -771,7 +872,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
                        kcd->prev = savcur;
                }
                else {
-                       kcd->cur = savcur;
+                       kcd->curr = savcur;
                        knife_add_single_cut(kcd);
                }
 
@@ -924,42 +1025,45 @@ static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
 
                glBegin(GL_LINES);
                glVertex3fv(kcd->prev.cage);
-               glVertex3fv(kcd->cur.cage);
+               glVertex3fv(kcd->curr.cage);
                glEnd();
 
                glLineWidth(1.0);
        }
 
-       if (kcd->cur.edge) {
+       if (kcd->curr.edge) {
                glColor3ubv(kcd->colors.edge);
                glLineWidth(2.0);
 
                glBegin(GL_LINES);
-               glVertex3fv(kcd->cur.edge->v1->cageco);
-               glVertex3fv(kcd->cur.edge->v2->cageco);
+               glVertex3fv(kcd->curr.edge->v1->cageco);
+               glVertex3fv(kcd->curr.edge->v2->cageco);
                glEnd();
 
                glLineWidth(1.0);
        }
-       else if (kcd->cur.vert) {
+       else if (kcd->curr.vert) {
                glColor3ubv(kcd->colors.point);
                glPointSize(11);
 
                glBegin(GL_POINTS);
-               glVertex3fv(kcd->cur.cage);
+               glVertex3fv(kcd->curr.cage);
                glEnd();
        }
 
-       if (kcd->cur.bmface) {
+       if (kcd->curr.bmface) {
                glColor3ubv(kcd->colors.curpoint);
                glPointSize(9);
 
                glBegin(GL_POINTS);
-               glVertex3fv(kcd->cur.cage);
+               glVertex3fv(kcd->curr.cage);
                glEnd();
        }
 
        if (kcd->totlinehit > 0) {
+               const float vthresh4 = kcd->vthresh / 4.0f;
+               const float vthresh4_squared = vthresh4 * vthresh4;
+
                BMEdgeHit *lh;
                int i;
 
@@ -978,12 +1082,12 @@ static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
                        knife_project_v3(kcd, lh->kfe->v2->cageco, sv2);
                        knife_project_v3(kcd, lh->cagehit, lh->schit);
 
-                       if (len_v2v2(lh->schit, sv1) < kcd->vthresh / 4.0f) {
+                       if (len_squared_v2v2(lh->schit, sv1) < vthresh4_squared) {
                                copy_v3_v3(lh->cagehit, lh->kfe->v1->cageco);
                                glVertex3fv(lh->cagehit);
                                lh->v = lh->kfe->v1;
                        }
-                       else if (len_v2v2(lh->schit, sv2) < kcd->vthresh / 4.0f) {
+                       else if (len_squared_v2v2(lh->schit, sv2) < vthresh4_squared) {
                                copy_v3_v3(lh->cagehit, lh->kfe->v2->cageco);
                                glVertex3fv(lh->cagehit);
                                lh->v = lh->kfe->v2;
@@ -1052,11 +1156,11 @@ static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
 
 static float len_v3_tri_side_max(const float v1[3], const float v2[3], const float v3[3])
 {
-       const float s1 = len_v3v3(v1, v2);
-       const float s2 = len_v3v3(v2, v3);
-       const float s3 = len_v3v3(v3, v1);
+       const float s1 = len_squared_v3v3(v1, v2);
+       const float s2 = len_squared_v3v3(v2, v3);
+       const float s3 = len_squared_v3v3(v3, v1);
 
-       return MAX3(s1, s2, s3);
+       return sqrtf(max_fff(s1, s2, s3));
 }
 
 static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree,
@@ -1068,13 +1172,13 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree,
        BLI_array_declare(edges);
        BVHTreeOverlap *results, *result;
        BMLoop **ls;
-       float cos[9], uv[3], lambda;
+       float cos[9], lambda;
        unsigned int tot = 0;
-       int i, j;
+       int i;
 
        /* for comparing distances, error of intersection depends on triangle scale.
         * need to scale down before squaring for accurate comparison */
-       const float depsilon = 50 * FLT_EPSILON * len_v3_tri_side_max(v1, v2, v3);
+       const float depsilon = (FLT_EPSILON / 2.0f) * len_v3_tri_side_max(v1, v2, v3);
        const float depsilon_squared = depsilon * depsilon;
 
        copy_v3_v3(cos + 0, v1);
@@ -1087,106 +1191,123 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree,
        result = results = BLI_bvhtree_overlap(tree, tree2, &tot);
 
        for (i = 0; i < tot; i++, result++) {
-               float p[3];
+               BMLoop *l1;
+               BMFace *hitf;
+               ListBase *lst;
+               Ref *ref;
 
                ls = (BMLoop **)kcd->em->looptris[result->indexA];
 
-               for (j = 0; j < 3; j++) {
-                       BMLoop *l1 = ls[j];
-                       BMFace *hitf;
-                       ListBase *lst = knife_get_face_kedges(kcd, l1->f);
-                       Ref *ref;
+               l1 = ls[0];
+               lst = knife_get_face_kedges(kcd, l1->f);
+
+               for (ref = lst->first; ref; ref = ref->next) {
+                       KnifeEdge *kfe = ref->ref;
 
-                       for (ref = lst->first; ref; ref = ref->next) {
-                               KnifeEdge *kfe = ref->ref;
+                       if (BLI_smallhash_haskey(ehash, (intptr_t)kfe)) {
+                               continue;  /* We already found a hit on this knife edge */
+                       }
 
-                               //if (kfe == kcd->cur.edge || kfe == kcd->prev.edge)
-                               //      continue;
+                       if (isect_line_tri_v3(kfe->v1->cageco, kfe->v2->cageco, v1, v2, v3, &lambda, NULL)) {
+                               float p[3], no[3], view[3], sp[3];
 
-                               if (isect_line_tri_v3(kfe->v1->cageco, kfe->v2->cageco, v1, v2, v3, &lambda, uv)) {
-                                       float no[3], view[3], sp[3];
+                               interp_v3_v3v3(p, kfe->v1->cageco, kfe->v2->cageco, lambda);
 
-                                       interp_v3_v3v3(p, kfe->v1->cageco, kfe->v2->cageco, lambda);
+                               if (kcd->curr.vert && len_squared_v3v3(kcd->curr.vert->cageco, p) < depsilon_squared) {
+                                       continue;
+                               }
+                               if (kcd->prev.vert && len_squared_v3v3(kcd->prev.vert->cageco, p) < depsilon_squared) {
+                                       continue;
+                               }
+                               if (len_squared_v3v3(kcd->prev.cage, p) < depsilon_squared ||
+                                   len_squared_v3v3(kcd->curr.cage, p) < depsilon_squared)
+                               {
+                                       continue;
+                               }
+                               if ((kcd->vc.rv3d->rflag & RV3D_CLIPPING) &&
+                                   ED_view3d_clipping_test(kcd->vc.rv3d, p, TRUE))
+                               {
+                                       continue;
+                               }
 
-                                       if (kcd->cur.vert && len_squared_v3v3(kcd->cur.vert->cageco, p) < depsilon_squared)
-                                               continue;
-                                       if (kcd->prev.vert && len_squared_v3v3(kcd->prev.vert->cageco, p) < depsilon_squared)
-                                               continue;
-                                       if (len_squared_v3v3(kcd->prev.cage, p) < depsilon_squared ||
-                                           len_squared_v3v3(kcd->cur.cage, p) < depsilon_squared)
-                                       {
-                                               continue;
-                                       }
+                               knife_project_v3(kcd, p, sp);
+                               ED_view3d_unproject(mats, view, sp[0], sp[1], 0.0f);
+                               mul_m4_v3(kcd->ob->imat, view);
 
-                                       knife_project_v3(kcd, p, sp);
-                                       view3d_unproject(mats, view, sp[0], sp[1], 0.0f);
-                                       mul_m4_v3(kcd->ob->imat, view);
+                               if (kcd->cut_through) {
+                                       hitf = FALSE;
+                               }
+                               else {
+                                       /* check if this point is visible in the viewport */
+                                       float p1[3], lambda1;
 
-                                       if (kcd->cut_through) {
-                                               hitf = FALSE;
+                                       /* if face isn't planer, p may be behind the current tesselated tri,
+                                        * so move it onto that and then a little towards eye */
+                                       if (isect_line_tri_v3(p, view, ls[0]->v->co, ls[1]->v->co, ls[2]->v->co, &lambda1, NULL)) {
+                                               interp_v3_v3v3(p1, p, view, lambda1);
                                        }
                                        else {
-                                               /* check if this point is visible in the viewport */
-                                               sub_v3_v3(view, p);
-                                               normalize_v3(view);
+                                               copy_v3_v3(p1, p);
+                                       }
+                                       sub_v3_v3(view, p1);
+                                       normalize_v3(view);
 
-                                               copy_v3_v3(no, view);
-                                               mul_v3_fl(no, 0.003);
+                                       copy_v3_v3(no, view);
+                                       mul_v3_fl(no, 0.003);
 
-                                               /* go towards view a bit */
-                                               add_v3_v3(p, no);
+                                       /* go towards view a bit */
+                                       add_v3_v3(p1, no);
+                                               
+                                       /* ray cast */
+                                       hitf = BMBVH_RayCast(bmtree, p1, no, NULL, NULL);
+                               }
 
-                                               /* ray cast */
-                                               hitf = BMBVH_RayCast(bmtree, p, no, NULL, NULL);
+                               /* ok, if visible add the new point */
+                               if (!hitf && !BLI_smallhash_haskey(ehash, (intptr_t)kfe)) {
+                                       BMEdgeHit hit;
+       
+                                       if (len_squared_v3v3(p, kcd->curr.co) < depsilon_squared ||
+                                           len_squared_v3v3(p, kcd->prev.co) < depsilon_squared)
+                                       {
+                                               continue;
                                        }
 
-                                       /* ok, if visible add the new point */
-                                       if (!hitf && !BLI_smallhash_haskey(ehash, (intptr_t)kfe)) {
-                                               BMEdgeHit hit;
-                                               
-                                               if (len_squared_v3v3(p, kcd->cur.co) < depsilon_squared ||
-                                                   len_squared_v3v3(p, kcd->prev.co) < depsilon_squared)
-                                               {
-                                                       continue;
-                                               }
+                                       hit.kfe = kfe;
+                                       hit.v = NULL;
+
+                                       knife_find_basef(kfe);
+                                       hit.f = kfe->basef;
+                                       hit.perc = len_v3v3(p, kfe->v1->cageco) / len_v3v3(kfe->v1->cageco, kfe->v2->cageco);
+                                       copy_v3_v3(hit.cagehit, p);
+
+                                       interp_v3_v3v3(p, kfe->v1->co, kfe->v2->co, hit.perc);
+                                       copy_v3_v3(hit.realhit, p);
+
+                                       /* BMESH_TODO: should also snap to vertices */
+                                       if (kcd->snap_midpoints) {
+                                               float perc = hit.perc;
 
-                                               hit.kfe = kfe;
-                                               hit.v = NULL;
-
-                                               knife_find_basef(kcd, kfe);
-                                               hit.f = kfe->basef;
-                                               hit.perc = len_v3v3(p, kfe->v1->cageco) / len_v3v3(kfe->v1->cageco, kfe->v2->cageco);
-                                               copy_v3_v3(hit.cagehit, p);
-
-                                               interp_v3_v3v3(p, kfe->v1->co, kfe->v2->co, hit.perc);
-                                               copy_v3_v3(hit.realhit, p);
-
-                                               /* BMESH_TODO: should also snap to vertices */
-                                               if (kcd->snap_midpoints) {
-                                                       float perc = hit.perc;
-
-                                                       /* select the closest from the edge endpoints or the midpoint */
-                                                       if (perc < 0.25f) {
-                                                               perc = 0.0f;
-                                                       }
-                                                       else if (perc < 0.75f) {
-                                                               perc = 0.5f;
-                                                       }
-                                                       else {
-                                                               perc = 1.0f;
-                                                       }
-
-                                                       interp_v3_v3v3(hit.hit, kfe->v1->co, kfe->v2->co, perc);
-                                                       interp_v3_v3v3(hit.cagehit, kfe->v1->cageco, kfe->v2->cageco, perc);
+                                               /* select the closest from the edge endpoints or the midpoint */
+                                               if (perc < 0.25f) {
+                                                       perc = 0.0f;
+                                               }
+                                               else if (perc < 0.75f) {
+                                                       perc = 0.5f;
                                                }
                                                else {
-                                                       copy_v3_v3(hit.hit, p);
+                                                       perc = 1.0f;
                                                }
-                                               knife_project_v3(kcd, hit.cagehit, hit.schit);
 
-                                               BLI_array_append(edges, hit);
-                                               BLI_smallhash_insert(ehash, (intptr_t)kfe, NULL);
+                                               interp_v3_v3v3(hit.hit, kfe->v1->co, kfe->v2->co, perc);
+                                               interp_v3_v3v3(hit.cagehit, kfe->v1->cageco, kfe->v2->cageco, perc);
+                                       }
+                                       else {
+                                               copy_v3_v3(hit.hit, p);
                                        }
+                                       knife_project_v3(kcd, hit.cagehit, hit.schit);
+
+                                       BLI_array_append(edges, hit);
+                                       BLI_smallhash_insert(ehash, (intptr_t)kfe, NULL);
                                }
                        }
                }
@@ -1208,6 +1329,34 @@ static void knife_bgl_get_mats(KnifeTool_OpData *UNUSED(kcd), bglMats *mats)
        //copy_m4_m4(mats->projection, kcd->vc.rv3d->winmat);
 }
 
+/* Calculate maximum excursion from (0,0,0) of mesh */
+static void calc_ortho_extent(KnifeTool_OpData *kcd)
+{
+       BMIter iter;
+       BMVert *v;
+       BMesh *bm = kcd->em->bm;
+       float max_xyz = 0.0f;
+       int i;
+
+       BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
+               for (i = 0; i < 3; i++)
+                       max_xyz = max_ff(max_xyz, fabs(v->co[i]));
+       }
+       kcd->ortho_extent = max_xyz;
+}
+
+/* Clip the line (v1, v2) to planes perpendicular to it and distances d from
+ * the closest point on the line to the origin */
+static void clip_to_ortho_planes(float v1[3], float v2[3], float d)
+{
+       float closest[3];
+       const float origin[3] = {0.0f, 0.0f, 0.0f};
+
+       closest_to_line_v3(closest, origin, v1, v2);
+       dist_ensure_v3_v3fl(v1, closest, d);
+       dist_ensure_v3_v3fl(v2, closest, d);
+}
+
 /* Finds visible (or all, if cutting through) edges that intersects the current screen drag line */
 static void knife_find_line_hits(KnifeTool_OpData *kcd)
 {
@@ -1226,7 +1375,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
        }
 
        copy_v3_v3(v1, kcd->prev.cage);
-       copy_v3_v3(v2, kcd->cur.cage);
+       copy_v3_v3(v2, kcd->curr.cage);
 
        /* project screen line's 3d coordinates back into 2d */
        knife_project_v3(kcd, v1, s1);
@@ -1250,8 +1399,10 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
         * (which may involve using doubles everywhere!),
         * limit the distance between these points */
        if (kcd->is_ortho) {
-               limit_dist_v3(v1, v3, 200.0f);
-               limit_dist_v3(v2, v4, 200.0f);
+               if (kcd->ortho_extent == 0.0f)
+                       calc_ortho_extent(kcd);
+               clip_to_ortho_planes(v1, v3, kcd->ortho_extent + 10.0f);
+               clip_to_ortho_planes(v2, v4, kcd->ortho_extent + 10.0f);
        }
 
        BLI_smallhash_init(ehash);
@@ -1281,19 +1432,18 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
        BLI_smallhash_release(ehash);
 }
 
-static void knife_input_ray_cast(KnifeTool_OpData *kcd, const int mval_i[2],
+/* this works but gives numeric problems [#33400] */
+#if 0
+static void knife_input_ray_cast(KnifeTool_OpData *kcd, const float mval[2],
                                  float r_origin[3], float r_ray[3])
 {
        bglMats mats;
-       float mval[2], imat[3][3];
+       float imat[3][3];
 
        knife_bgl_get_mats(kcd, &mats);
 
-       mval[0] = (float)mval_i[0];
-       mval[1] = (float)mval_i[1];
-
        /* unproject to find view ray */
-       view3d_unproject(&mats, r_origin, mval[0], mval[1], 0.0f);
+       ED_view3d_unproject(&mats, r_origin, mval[0], mval[1], 0.0f);
 
        if (kcd->is_ortho) {
                negate_v3_v3(r_ray, kcd->vc.rv3d->viewinv[2]);
@@ -1311,17 +1461,37 @@ static void knife_input_ray_cast(KnifeTool_OpData *kcd, const int mval_i[2],
        mul_m4_v3(kcd->ob->imat, r_origin);
        mul_m3_v3(imat, r_ray);
 }
+#endif
+
+static void knife_input_ray_segment(KnifeTool_OpData *kcd, const float mval[2], const float ofs,
+                                    float r_origin[3], float r_origin_ofs[3])
+{
+       bglMats mats;
+
+       knife_bgl_get_mats(kcd, &mats);
+
+       /* unproject to find view ray */
+       ED_view3d_unproject(&mats, r_origin,     mval[0], mval[1], 0.0f);
+       ED_view3d_unproject(&mats, r_origin_ofs, mval[0], mval[1], ofs);
+
+       /* transform into object space */
+       invert_m4_m4(kcd->ob->imat, kcd->ob->obmat); 
+
+       mul_m4_v3(kcd->ob->imat, r_origin);
+       mul_m4_v3(kcd->ob->imat, r_origin_ofs);
+}
 
 static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float co[3], float cageco[3], int *is_space)
 {
        BMFace *f;
-       int dist = KMAXDIST;
+       float dist = KMAXDIST;
        float origin[3];
+       float origin_ofs[3];
        float ray[3];
 
        /* unproject to find view ray */
-       knife_input_ray_cast(kcd, kcd->vc.mval, origin, ray);
-       add_v3_v3v3(co, origin, ray);
+       knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
+       sub_v3_v3v3(ray, origin_ofs, origin);
 
        f = BMBVH_RayCast(kcd->bmbvh, origin, ray, co, cageco);
 
@@ -1372,12 +1542,7 @@ static int knife_sample_screen_density(KnifeTool_OpData *kcd, float radius)
                                dis = len_v2v2(kfv->sco, sco);
                                if (dis < radius) {
                                        if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
-                                               float vec[3];
-
-                                               copy_v3_v3(vec, kfv->cageco);
-                                               mul_m4_v3(kcd->vc.obedit->obmat, vec);
-
-                                               if (ED_view3d_clipping_test(kcd->vc.rv3d, vec, TRUE) == 0) {
+                                               if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, TRUE) == 0) {
                                                        c++;
                                                }
                                        }
@@ -1400,9 +1565,10 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
 {
        float density = (float)knife_sample_screen_density(kcd, maxsize * 2.0f);
 
-       density = MAX2(density, 1);
+       if (density < 1.0f)
+               density = 1.0f;
 
-       return MIN2(maxsize / (density * 0.5f), maxsize);
+       return min_ff(maxsize / (density * 0.5f), maxsize);
 }
 
 /* p is closest point on edge to the mouse cursor */
@@ -1421,7 +1587,7 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo
        copy_v3_v3(p, co);
        copy_v3_v3(cagep, cageco);
 
-       kcd->cur.bmface = f;
+       kcd->curr.bmface = f;
 
        if (f) {
                KnifeEdge *cure = NULL;
@@ -1443,13 +1609,10 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo
                        dis = dist_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco);
                        if (dis < curdis && dis < maxdist) {
                                if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
-                                       float labda = labda_PdistVL2Dfl(sco, kfe->v1->sco, kfe->v2->sco);
+                                       float lambda = line_point_factor_v2(sco, kfe->v1->sco, kfe->v2->sco);
                                        float vec[3];
 
-                                       vec[0] = kfe->v1->cageco[0] + labda * (kfe->v2->cageco[0] - kfe->v1->cageco[0]);
-                                       vec[1] = kfe->v1->cageco[1] + labda * (kfe->v2->cageco[1] - kfe->v1->cageco[1]);
-                                       vec[2] = kfe->v1->cageco[2] + labda * (kfe->v2->cageco[2] - kfe->v1->cageco[2]);
-                                       mul_m4_v3(kcd->vc.obedit->obmat, vec);
+                                       interp_v3_v3v3(vec, kfe->v1->cageco, kfe->v2->cageco, lambda);
 
                                        if (ED_view3d_clipping_test(kcd->vc.rv3d, vec, TRUE) == 0) {
                                                cure = kfe;
@@ -1468,6 +1631,8 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo
 
                if (cure && p) {
                        if (!kcd->ignore_edge_snapping || !(cure->e)) {
+                               KnifeVert *edgesnap = NULL;
+
                                if (kcd->snap_midpoints) {
                                        mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
                                        mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
@@ -1479,6 +1644,13 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo
                                        d = len_v3v3(cagep, cure->v1->cageco) / len_v3v3(cure->v1->cageco, cure->v2->cageco);
                                        interp_v3_v3v3(p, cure->v1->co, cure->v2->co, d);
                                }
+
+                               /* update mouse coordinates to the snapped-to edge's screen coordinates
+                                * this is important for angle snap, which uses the previous mouse position */
+                               edgesnap = new_knife_vert(kcd, p, cagep);
+                               kcd->curr.mval[0] = edgesnap->sco[0];
+                               kcd->curr.mval[1] = edgesnap->sco[1];
+
                        }
                        else {
                                return NULL;
@@ -1509,7 +1681,7 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo
        /* set p to co, in case we don't find anything, means a face cut */
        copy_v3_v3(p, co);
        copy_v3_v3(cagep, p);
-       kcd->cur.bmface = f;
+       kcd->curr.bmface = f;
 
        if (f) {
                ListBase *lst;
@@ -1532,12 +1704,7 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo
                                dis = len_v2v2(kfv->sco, sco);
                                if (dis < curdis && dis < maxdist) {
                                        if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) {
-                                               float vec[3];
-
-                                               copy_v3_v3(vec, kfv->cageco);
-                                               mul_m4_v3(kcd->vc.obedit->obmat, vec);
-
-                                               if (ED_view3d_clipping_test(kcd->vc.rv3d, vec, TRUE) == 0) {
+                                               if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, TRUE) == 0) {
                                                        curv = kfv;
                                                        curdis = dis;
                                                }
@@ -1557,6 +1724,11 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo
                        if (curv && p) {
                                copy_v3_v3(p, curv->co);
                                copy_v3_v3(cagep, curv->cageco);
+
+                               /* update mouse coordinates to the snapped-to vertex's screen coordinates
+                                * this is important for angle snap, which uses the previous mouse position */
+                               kcd->curr.mval[0] = curv->sco[0];
+                               kcd->curr.mval[1] = curv->sco[1];
                        }
 
                        return curv;
@@ -1575,64 +1747,76 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo
        return NULL;
 }
 
+/* update both kcd->curr.mval and kcd->vc.mval to snap to required angle */
 static void knife_snap_angle(KnifeTool_OpData *kcd)
 {
-       int dx, dy;
+       float dx, dy;
        float w, abs_tan;
 
-       dx = kcd->vc.mval[0] - kcd->prev.mval[0];
-       dy = kcd->vc.mval[1] - kcd->prev.mval[1];
-       if (dx == 0 || dy == 0)
+       dx = kcd->curr.mval[0] - kcd->prev.mval[0];
+       dy = kcd->curr.mval[1] - kcd->prev.mval[1];
+       if (dx == 0.0f && dy == 0.0f)
                return;
 
-       w = (float)dy / (float)dx;
+       if (dx == 0.0f) {
+               kcd->angle_snapping = ANGLE_90;
+               kcd->curr.mval[0] = kcd->prev.mval[0];
+       }
+
+       w = dy / dx;
        abs_tan = fabsf(w);
        if (abs_tan <= 0.4142f) { /* tan(22.5 degrees) = 0.4142 */
                kcd->angle_snapping = ANGLE_0;
-               kcd->vc.mval[1] = kcd->prev.mval[1];
+               kcd->curr.mval[1] = kcd->prev.mval[1];
        }
        else if (abs_tan < 2.4142f) { /* tan(67.5 degrees) = 2.4142 */
                if (w > 0) {
                        kcd->angle_snapping = ANGLE_45;
-                       kcd->vc.mval[1] = kcd->prev.mval[1] + dx;
+                       kcd->curr.mval[1] = kcd->prev.mval[1] + dx;
                }
                else {
                        kcd->angle_snapping = ANGLE_135;
-                       kcd->vc.mval[1] = kcd->prev.mval[1] - dx;
+                       kcd->curr.mval[1] = kcd->prev.mval[1] - dx;
                }
        }
        else {
                kcd->angle_snapping = ANGLE_90;
-               kcd->vc.mval[0] = kcd->prev.mval[0];
+               kcd->curr.mval[0] = kcd->prev.mval[0];
        }
+
+       kcd->vc.mval[0] = round_ftoi(kcd->curr.mval[0]);
+       kcd->vc.mval[1] = round_ftoi(kcd->curr.mval[1]);
 }
 
 /* update active knife edge/vert pointers */
 static int knife_update_active(KnifeTool_OpData *kcd)
 {
+       knife_pos_data_clear(&kcd->curr);
+       kcd->curr.mval[0] = (float)kcd->vc.mval[0];
+       kcd->curr.mval[1] = (float)kcd->vc.mval[1];
        if (kcd->angle_snapping != ANGLE_FREE && kcd->mode == MODE_DRAGGING)
                knife_snap_angle(kcd);
 
-       knife_pos_data_clear(&kcd->cur);
-       kcd->cur.mval[0] = kcd->vc.mval[0];
-       kcd->cur.mval[1] = kcd->vc.mval[1];
+       /* XXX knife_snap_angle updates the view coordinate mouse values to constrained angles,
+        * which current mouse values are set to current mouse values are then used
+        * for vertex and edge snap detection, without regard to the exact angle constraint */
+       kcd->curr.vert = knife_find_closest_vert(kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space);
 
-       kcd->cur.vert = knife_find_closest_vert(kcd, kcd->cur.co, kcd->cur.cage, &kcd->cur.bmface, &kcd->cur.is_space);
-       if (!kcd->cur.vert) {
-               kcd->cur.edge = knife_find_closest_edge(kcd, kcd->cur.co, kcd->cur.cage, &kcd->cur.bmface, &kcd->cur.is_space);
+       if (!kcd->curr.vert) {
+               kcd->curr.edge = knife_find_closest_edge(kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space);
        }
 
        /* if no hits are found this would normally default to (0, 0, 0) so instead
         * get a point at the mouse ray closest to the previous point.
         * Note that drawing lines in `free-space` isn't properly supported
         * but theres no guarantee (0, 0, 0) has any geometry either - campbell */
-       if (kcd->cur.vert == NULL && kcd->cur.edge == NULL) {
-               float origin[3], ray[3], co[3];
+       if (kcd->curr.vert == NULL && kcd->curr.edge == NULL) {
+               float origin[3];
+               float origin_ofs[3];
 
-               knife_input_ray_cast(kcd, kcd->vc.mval, origin, ray);
-               add_v3_v3v3(co, origin, ray);
+               knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs);
 
-               closest_to_line_v3(kcd->cur.cage, kcd->prev.cage, co, origin);
+               closest_to_line_v3(kcd->curr.cage, kcd->prev.cage, origin_ofs, origin);
        }
 
        if (kcd->mode == MODE_DRAGGING) {
@@ -1641,6 +1825,9 @@ static int knife_update_active(KnifeTool_OpData *kcd)
        return 1;
 }
 
+#define SCANFILL_CUTS 0
+#if SCANFILL_CUTS
+
 #define MARK            4
 #define DEL             8
 #define VERT_ON_EDGE    16
@@ -1649,9 +1836,6 @@ static int knife_update_active(KnifeTool_OpData *kcd)
 #define BOUNDARY        128
 #define FACE_NEW        256
 
-#define SCANFILL_CUTS 0
-#if SCANFILL_CUTS
-
 typedef struct facenet_entry {
        struct facenet_entry *next, *prev;
        KnifeEdge *kfe;
@@ -1679,10 +1863,10 @@ static void remerge_faces(KnifeTool_OpData *kcd)
        BMOperator bmop;
        int idx;
 
-       BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", FACE_NEW, BOUNDARY);
+       BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff edges=%Fe", FACE_NEW, BOUNDARY);
 
        BMO_op_exec(bm, &bmop);
-       BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", BM_FACE, FACE_NEW);
+       BMO_slot_buffer_flag_enable(bm, &bmop, "geom.out", BM_FACE, FACE_NEW);
 
        BMO_op_finish(bm, &bmop);
 
@@ -1810,21 +1994,21 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd)
                i++;
 
                if (kfe->e && kfe->v1->v == kfe->e->v1 && kfe->v2->v == kfe->e->v2) {
-                       kfe->oe = kfe->e;
+                       kfe->e_old = kfe->e;
                        continue;
                }
 
                j++;
 
                if (kfe->e) {
-                       kfe->oe = kfe->e;
+                       kfe->e_old = kfe->e;
 
                        BMO_elem_flag_enable(bm, kfe->e, DEL);
                        BMO_elem_flag_disable(bm, kfe->e, BOUNDARY);
                        kfe->e = NULL;
                }
 
-               kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, TRUE);
+               kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, NULL, BM_CREATE_NO_DOUBLE);
                BMO_elem_flag_enable(bm, kfe->e, BOUNDARY);
 
                for (ref = kfe->faces.first; ref; ref = ref->next) {
@@ -1843,13 +2027,13 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd)
 
                if (!kfe->v1 || !kfe->v2 || kfe->v1->inspace || kfe->v2->inspace)
                        continue;
-               if (!(kfe->oe && kfe->v1->v == kfe->oe->v1 && kfe->v2->v == kfe->oe->v2))
+               if (!(kfe->e_old && kfe->v1->v == kfe->e_old->v1 && kfe->v2->v == kfe->e_old->v2))
                        continue;
 
                k++;
 
                BMO_elem_flag_enable(bm, kfe->e, BOUNDARY);
-               kfe->oe = kfe->e;
+               kfe->e_old = kfe->e;
 
                for (ref = kfe->faces.first; ref; ref = ref->next) {
                        f = ref->ref;
@@ -1869,7 +2053,7 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd)
                ScanFillFace *sf_tri;
                ScanFillVert *sf_vert, *sf_vert_last;
                int j;
-               float rndscale = FLT_EPSILON * 25;
+               float rndscale = (KNIFE_FLT_EPS / 4.0f);
 
                f = faces[i];
                BLI_smallhash_init(hash);
@@ -1912,7 +2096,7 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd)
                        if (sf_vert->poly_nr > 1 && sf_vert_last->poly_nr > 1) {
                                ScanFillEdge *sf_edge;
                                sf_edge = BLI_scanfill_edge_add(&sf_ctx, sf_vert_last, sf_vert);
-                               if (entry->kfe->oe)
+                               if (entry->kfe->e_old)
                                        sf_edge->f = SF_EDGE_BOUNDARY;  /* mark as original boundary edge */
 
                                BMO_elem_flag_disable(bm, entry->kfe->e->v1, DEL);
@@ -1926,7 +2110,7 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd)
                        }
                }
 
-               BLI_scanfill_calc(&sf_ctx, FALSE);
+               BLI_scanfill_calc(&sf_ctx, 0);
 
                for (sf_tri = sf_ctx.fillfacebase.first; sf_tri; sf_tri = sf_tri->next) {
                        BMVert *v1 = sf_tri->v3->tmp.p, *v2 = sf_tri->v2->tmp.p, *v3 = sf_tri->v1->tmp.p;
@@ -1989,9 +2173,9 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd)
        remerge_faces(kcd);
 
        /* delete left over faces */
-       BMO_op_callf(bm, "del geom=%ff context=%i", DEL, DEL_ONLYFACES);
-       BMO_op_callf(bm, "del geom=%fe context=%i", DEL, DEL_EDGES);
-       BMO_op_callf(bm, "del geom=%fv context=%i", DEL, DEL_VERTS);
+       BMO_op_callf(bm, BMO_FLAG_DEFAULTS, "delete geom=%ff context=%i", DEL, DEL_ONLYFACES);
+       BMO_op_callf(bm, BMO_FLAG_DEFAULTS, "delete geom=%fe context=%i", DEL, DEL_EDGES);
+       BMO_op_callf(bm, BMO_FLAG_DEFAULTS, "delete geom=%fv context=%i", DEL, DEL_VERTS);
 
        if (face_nets) 
                MEM_freeN(face_nets);
@@ -2247,7 +2431,7 @@ static int find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, Li
        ListBase *chain;
        BMVert *v;
        BMIter iter;
-       int nh, nf, i, j, k, m, ax, ay, ok, sep, bestsep;
+       int nh, nf, i, j, k, m, ax, ay, ok, sep = 0 /* Quite warnings */, bestsep;
        int besti[2], bestj[2];
        float d, bestd;
 
@@ -2430,10 +2614,8 @@ static void knife_make_chain_cut(KnifeTool_OpData *kcd, BMFace *f, ListBase *cha
        BMLoop *lnew, *l_iter;
        int i;
        int nco = BLI_countlist(chain) - 1;
-       float (*cos)[3] = NULL;
-       KnifeVert **kverts;
-       BLI_array_fixedstack_declare(cos, BM_NGON_STACK_SIZE, nco, __func__);
-       BLI_array_fixedstack_declare(kverts, BM_NGON_STACK_SIZE, nco, __func__);
+       float (*cos)[3] = BLI_array_alloca(cos, nco);
+       KnifeVert **kverts = BLI_array_alloca(kverts, nco);
 
        kfe = ((Ref *)chain->first)->ref;
        v1 = kfe->v1->v ? kfe->v1->v : kfe->v2->v;
@@ -2482,9 +2664,6 @@ static void knife_make_chain_cut(KnifeTool_OpData *kcd, BMFace *f, ListBase *cha
                        BM_edge_select_set(bm, lnew->e, TRUE);
                }
        }
-
-       BLI_array_fixedstack_free(cos);
-       BLI_array_fixedstack_free(kverts);
 }
 
 static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges)
@@ -2674,7 +2853,7 @@ static void knife_make_cuts(KnifeTool_OpData *kcd)
 #endif
 
 /* called on tool confirmation */
-static void knifetool_finish(bContext *C, wmOperator *op)
+static void knifetool_finish(wmOperator *op)
 {
        KnifeTool_OpData *kcd = op->customdata;
 
@@ -2685,7 +2864,7 @@ static void knifetool_finish(bContext *C, wmOperator *op)
 #endif
 
        EDBM_mesh_normals_update(kcd->em);
-       EDBM_update_generic(C, kcd->em, TRUE);
+       EDBM_update_generic(kcd->em, TRUE, TRUE);
 }
 
 /* copied from paint_image.c */
@@ -2765,11 +2944,11 @@ static void cage_mapped_verts_callback(void *userData, int index, const float co
        }
 }
 
-static void knifetool_update_mval(KnifeTool_OpData *kcd, int mval[2])
+static void knifetool_update_mval(KnifeTool_OpData *kcd, const int mval_i[2])
 {
        knife_recalc_projmat(kcd);
-       kcd->vc.mval[0] = mval[0];
-       kcd->vc.mval[1] = mval[1];
+       kcd->vc.mval[0] = mval_i[0];
+       kcd->vc.mval[1] = mval_i[1];
 
        if (knife_update_active(kcd)) {
                ED_region_tag_redraw(kcd->ar);
@@ -2840,7 +3019,7 @@ static int knifetool_init(bContext *C, wmOperator *op, int UNUSED(do_cut))
        /* can't usefully select resulting edges in face mode */
        kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE);
 
-       knife_pos_data_clear(&kcd->cur);
+       knife_pos_data_clear(&kcd->curr);
        knife_pos_data_clear(&kcd->prev);
 
        knife_init_colors(&kcd->colors);
@@ -2855,7 +3034,7 @@ static int knifetool_cancel(bContext *C, wmOperator *op)
        return OPERATOR_CANCELLED;
 }
 
-static int knifetool_invoke(bContext *C, wmOperator *op, wmEvent *evt)
+static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        KnifeTool_OpData *kcd;
 
@@ -2869,7 +3048,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, wmEvent *evt)
        WM_event_add_modal_handler(C, op);
 
        kcd = op->customdata;
-       knifetool_update_mval(kcd, evt->mval);
+       knifetool_update_mval(kcd, event->mval);
 
        knife_update_header(C, kcd);
 
@@ -2902,7 +3081,8 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
                {KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""},
                {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
                {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""},
-               {0, NULL, 0, NULL, NULL}};
+               {0, NULL, 0, NULL, NULL}
+       };
 
        wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Knife Tool Modal Map");
 
@@ -2939,16 +3119,12 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
        return keymap;
 }
 
-static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event)
+static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
-       Object *obedit;
+       Object *obedit = CTX_data_edit_object(C);
        KnifeTool_OpData *kcd = op->customdata;
+       int do_refresh = FALSE;
 
-       if (!C) {
-               return OPERATOR_FINISHED;
-       }
-
-       obedit = CTX_data_edit_object(C);
        if (!obedit || obedit->type != OB_MESH || BMEdit_FromObject(obedit) != kcd->em) {
                knifetool_exit(C, op);
                ED_area_headerprint(CTX_wm_area(C), NULL);
@@ -2956,6 +3132,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event)
        }
 
        view3d_operator_needs_opengl(C);
+       ED_view3d_init_mats_rv3d(obedit, kcd->vc.rv3d);  /* needed to initialize clipping */
 
        if (kcd->mode == MODE_PANNING)
                kcd->mode = kcd->prevmode;
@@ -2975,7 +3152,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event)
                                /* finish */
                                ED_region_tag_redraw(kcd->ar);
 
-                               knifetool_finish(C, op);
+                               knifetool_finish(op);
                                knifetool_exit(C, op);
                                ED_area_headerprint(CTX_wm_area(C), NULL);
 
@@ -2987,6 +3164,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event)
                                knife_update_active(kcd);
                                knife_update_header(C, kcd);
                                ED_region_tag_redraw(kcd->ar);
+                               do_refresh = TRUE;
                                break;
                        case KNF_MODAL_MIDPOINT_OFF:
                                kcd->snap_midpoints = 0;
@@ -2995,24 +3173,29 @@ static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event)
                                knife_update_active(kcd);
                                knife_update_header(C, kcd);
                                ED_region_tag_redraw(kcd->ar);
+                               do_refresh = TRUE;
                                break;
                        case KNF_MODEL_IGNORE_SNAP_ON:
                                ED_region_tag_redraw(kcd->ar);
                                kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = 1;
                                knife_update_header(C, kcd);
+                               do_refresh = TRUE;
                                break;
                        case KNF_MODEL_IGNORE_SNAP_OFF:
                                ED_region_tag_redraw(kcd->ar);
                                kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = 0;
                                knife_update_header(C, kcd);
+                               do_refresh = TRUE;
                                break;
                        case KNF_MODAL_ANGLE_SNAP_TOGGLE:
                                kcd->angle_snapping = !kcd->angle_snapping;
                                knife_update_header(C, kcd);
+                               do_refresh = TRUE;
                                break;
                        case KNF_MODAL_CUT_THROUGH_TOGGLE:
                                kcd->cut_through = !kcd->cut_through;
                                knife_update_header(C, kcd);
+                               do_refresh = TRUE;
                                break;
                        case KNF_MODAL_NEW_CUT:
                                ED_region_tag_redraw(kcd->ar);
@@ -3065,6 +3248,12 @@ static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event)
                }
        }
 
+       if (do_refresh) {
+               /* we don't really need to update mval,
+                * but this happens to be the best way to refresh at the moment */
+               knifetool_update_mval(kcd, event->mval);
+       }
+
        /* keep going until the user confirms */
        return OPERATOR_RUNNING_MODAL;
 }