code cleanup: use const events for modal and invoke operators.
[blender.git] / source / blender / editors / mesh / editmesh_knife.c
index 8f08c3afc365a5ed817ecaa57bf5303a0adbaca0..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];
@@ -101,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 {
@@ -127,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 */
@@ -176,6 +179,7 @@ typedef struct KnifeTool_OpData {
        char select_result;  /* set on initialization */
 
        short is_ortho;
+       float ortho_extent;
        float clipsta, clipend;
 
        enum {
@@ -201,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)
@@ -232,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)
@@ -373,14 +386,13 @@ static void knife_start_cut(KnifeTool_OpData *kcd)
 
        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->curr.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->curr.cage, kcd->prev.cage);
                        copy_v3_v3(kcd->curr.co, kcd->prev.co);
@@ -428,7 +440,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float
        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,8 +456,8 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float
        }
        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 */
+                * 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);
@@ -555,7 +567,7 @@ static int verge_linehit(const void *vlh1, const void *vlh2)
 }
 
 /* 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 */
+ * 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;
@@ -563,9 +575,12 @@ static int find_connected_linehit(KnifeTool_OpData *kcd, int testi, BMFace *f, 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))
+                                                  &kcd->linehits[i].kfe->faces))
+                       {
                                return i;
-               } else if (f) {
+                       }
+               }
+               else if (f) {
                        if (find_ref(&kcd->linehits[i].kfe->faces, f))
                                return i;
                }
@@ -582,9 +597,9 @@ static void knife_sort_linehits(KnifeTool_OpData *kcd)
 
        /* 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 (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) > 80*FLT_EPSILON)
+                       if (fabsf(kcd->linehits[j].l - kcd->linehits[i].l) > KNIFE_FLT_EPS)
                                break;
                }
                nexti = j;
@@ -633,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);
@@ -642,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)
@@ -708,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) {
@@ -738,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) {
@@ -768,6 +789,7 @@ static void knife_cut_through(KnifeTool_OpData *kcd)
        kcd->totlinehit = 0;
 
        /* set up for next cut */
+       kcd->curr.vert = lastv;
        kcd->prev = kcd->curr;
 }
 
@@ -792,7 +814,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
                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;
@@ -811,13 +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->curr.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) < FLT_EPSILON * 80)
+                       if (!lastlh && fabsf(lh->l) < KNIFE_FLT_EPS)
                                continue;
 
                        if (kcd->prev.is_space) {
@@ -838,7 +860,7 @@ static void knife_add_cut(KnifeTool_OpData *kcd)
                        copy_v3_v3(kcd->curr.cage, lh->cagehit);
 
                        /* don't draw edges down faces parallel to view */
-                       if (lastlh && fabsf(lastlh->l - lh->l) < FLT_EPSILON * 80) {
+                       if (lastlh && fabsf(lastlh->l - lh->l) < KNIFE_FLT_EPS) {
                                kcd->prev = kcd->curr;
                                continue;
                        }
@@ -1039,6 +1061,9 @@ static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
        }
 
        if (kcd->totlinehit > 0) {
+               const float vthresh4 = kcd->vthresh / 4.0f;
+               const float vthresh4_squared = vthresh4 * vthresh4;
+
                BMEdgeHit *lh;
                int i;
 
@@ -1057,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;
@@ -1131,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,
@@ -1153,7 +1178,7 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree,
 
        /* 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);
@@ -1166,7 +1191,6 @@ 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;
@@ -1185,7 +1209,7 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree,
                        }
 
                        if (isect_line_tri_v3(kfe->v1->cageco, kfe->v2->cageco, v1, v2, v3, &lambda, NULL)) {
-                               float no[3], view[3], sp[3];
+                               float p[3], no[3], view[3], sp[3];
 
                                interp_v3_v3v3(p, kfe->v1->cageco, kfe->v2->cageco, lambda);
 
@@ -1200,9 +1224,14 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree,
                                {
                                        continue;
                                }
+                               if ((kcd->vc.rv3d->rflag & RV3D_CLIPPING) &&
+                                   ED_view3d_clipping_test(kcd->vc.rv3d, p, TRUE))
+                               {
+                                       continue;
+                               }
 
                                knife_project_v3(kcd, p, sp);
-                               view3d_unproject(mats, view, sp[0], sp[1], 0.0f);
+                               ED_view3d_unproject(mats, view, sp[0], sp[1], 0.0f);
                                mul_m4_v3(kcd->ob->imat, view);
 
                                if (kcd->cut_through) {
@@ -1300,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)
 {
@@ -1342,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);
@@ -1373,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]);
@@ -1403,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);
 
@@ -1464,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++;
                                                }
                                        }
@@ -1495,7 +1568,7 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
        if (density < 1.0f)
                density = 1.0f;
 
-       return minf(maxsize / (density * 0.5f), maxsize);
+       return min_ff(maxsize / (density * 0.5f), maxsize);
 }
 
 /* p is closest point on edge to the mouse cursor */
@@ -1536,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;
@@ -1578,8 +1648,8 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo
                                /* 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] = (int)edgesnap->sco[0];
-                               kcd->curr.mval[1] = (int)edgesnap->sco[1];
+                               kcd->curr.mval[0] = edgesnap->sco[0];
+                               kcd->curr.mval[1] = edgesnap->sco[1];
 
                        }
                        else {
@@ -1634,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;
                                                }
@@ -1662,8 +1727,8 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo
 
                                /* 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] = (int)curv->sco[0];
-                               kcd->curr.mval[1] = (int)curv->sco[1];
+                               kcd->curr.mval[0] = curv->sco[0];
+                               kcd->curr.mval[1] = curv->sco[1];
                        }
 
                        return curv;
@@ -1682,48 +1747,56 @@ 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->curr);
-       kcd->curr.mval[0] = kcd->vc.mval[0];
-       kcd->curr.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 */
@@ -1738,12 +1811,12 @@ static int knife_update_active(KnifeTool_OpData *kcd)
         * 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->curr.vert == NULL && kcd->curr.edge == NULL) {
-               float origin[3], ray[3], co[3];
+               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->curr.cage, kcd->prev.cage, co, origin);
+               closest_to_line_v3(kcd->curr.cage, kcd->prev.cage, origin_ofs, origin);
        }
 
        if (kcd->mode == MODE_DRAGGING) {
@@ -1752,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
@@ -1760,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;
@@ -1790,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);
 
@@ -1921,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) {
@@ -1954,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;
@@ -1980,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);
@@ -2023,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);
@@ -2037,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;
@@ -2541,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;
@@ -2593,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)
@@ -2785,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;
 
@@ -2796,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 */
@@ -2876,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);
@@ -2966,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;
 
@@ -2980,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);
 
@@ -3051,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);
@@ -3068,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;
@@ -3087,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);
 
@@ -3099,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;
@@ -3107,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);
@@ -3177,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;
 }