diff options
Diffstat (limited to 'source/blender/editors/mesh/editmesh_knife.c')
-rw-r--r-- | source/blender/editors/mesh/editmesh_knife.c | 726 |
1 files changed, 502 insertions, 224 deletions
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index b7b71be0df9..57d3f7406ca 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -37,15 +37,15 @@ #include "BLI_blenlib.h" #include "BLI_array.h" +#include "BLI_linklist.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 */ @@ -59,7 +59,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" @@ -92,7 +91,8 @@ typedef struct KnifeVert { ListBase faces; float co[3], cageco[3], sco[3]; /* sco is screen coordinates for cageco */ - short flag, draw, isface, inspace; + bool is_face, in_space; + bool draw; } KnifeVert; typedef struct Ref { @@ -104,9 +104,9 @@ typedef struct KnifeEdge { KnifeVert *v1, *v2; BMFace *basef; /* face to restrict face fill to */ 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 */ + bool draw; } KnifeEdge; typedef struct BMEdgeHit { @@ -130,16 +130,17 @@ typedef struct KnifePosData { KnifeVert *vert; KnifeEdge *edge; BMFace *bmface; - int is_space; + bool 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 */ typedef struct KnifeTool_OpData { ARegion *ar; /* region that knifetool was activated in */ void *draw_handle; /* for drawing preview loop */ - ViewContext vc; + ViewContext vc; /* note: _don't_ use 'mval', instead use the one we define below */ + float mval[2]; /* mouse value with snapping applied */ //bContext *C; Object *ob; @@ -175,12 +176,15 @@ typedef struct KnifeTool_OpData { KnifeColors colors; + /* run by the UI or not */ + bool is_interactive; + /* operatpr options */ - char cut_through; /* preference, can be modified at runtime (that feature may go) */ - char only_select; /* set on initialization */ - char select_result; /* set on initialization */ + bool cut_through; /* preference, can be modified at runtime (that feature may go) */ + bool only_select; /* set on initialization */ + bool select_result; /* set on initialization */ - short is_ortho; + bool is_ortho; float ortho_extent; float clipsta, clipend; @@ -191,8 +195,10 @@ typedef struct KnifeTool_OpData { MODE_PANNING } mode; - int snap_midpoints, prevmode, extend; - int ignore_edge_snapping, ignore_vert_snapping; + int prevmode; + bool snap_midpoints, extend; + bool ignore_edge_snapping; + bool ignore_vert_snapping; enum { ANGLE_FREE, @@ -208,31 +214,38 @@ typedef struct KnifeTool_OpData { static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f); #if 0 -static void knife_input_ray_cast(KnifeTool_OpData *kcd, const int mval_i[2], +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 int mval_i[2], const float ofs, +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); } +#if 0 +BLI_INLINE int round_ftoi(float x) +{ + return x > 0.0f ? (int)(x + 0.5f) : (int)(x - 0.5f); +} +#endif -static void knife_project_v3(KnifeTool_OpData *kcd, const float co[3], float sco[3]) +static void knife_project_v3(const KnifeTool_OpData *kcd, const float co[3], float sco[3]) { - ED_view3d_project_float_v3_m4(kcd->ar, co, sco, kcd->projmat); + ED_view3d_project_float_v3_m4(kcd->ar, co, sco, (float (*)[4])kcd->projmat); } static void knife_pos_data_clear(KnifePosData *kpd) @@ -242,8 +255,7 @@ 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; + zero_v2(kpd->mval); } static ListBase *knife_empty_list(KnifeTool_OpData *kcd) @@ -491,7 +503,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd) if (kcd->prev.edge && kcd->prev.edge == kcd->curr.edge) return; - kfe->draw = 1; + kfe->draw = true; if (kcd->prev.vert) { kfe->v1 = kcd->prev.vert; @@ -502,9 +514,9 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd) else { kfe->v1 = new_knife_vert(kcd, kcd->prev.co, kcd->prev.co); kfe->v1->draw = kfe->draw = !kcd->prev.is_space; - kfe->v1->inspace = kcd->prev.is_space; + kfe->v1->in_space = kcd->prev.is_space; kfe->draw = !kcd->prev.is_space; - kfe->v1->isface = 1; + kfe->v1->is_face = true; if (kfe->v1->draw && kcd->prev.bmface) knife_append_list(kcd, &kfe->v1->faces, kcd->prev.bmface); } @@ -519,13 +531,13 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd) else { 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->curr.is_space; + kfe->v2->is_face = true; + kfe->v2->in_space = kcd->curr.is_space; if (kfe->v2->draw && kcd->curr.bmface) knife_append_list(kcd, &kfe->v2->faces, kcd->curr.bmface); if (kcd->curr.is_space) - kfe->draw = 0; + kfe->draw = false; kcd->curr.vert = kfe->v2; } @@ -629,11 +641,10 @@ static void knife_add_single_cut_through(KnifeTool_OpData *kcd, KnifeVert *v1, K KnifeEdge *kfenew; kfenew = new_knife_edge(kcd); - kfenew->draw = 1; kfenew->basef = f; kfenew->v1 = v1; kfenew->v2 = v2; - kfenew->draw = 1; + kfenew->draw = true; knife_add_to_vert_edges(kcd, kfenew); @@ -645,8 +656,9 @@ static void knife_get_vert_faces(KnifeTool_OpData *kcd, KnifeVert *kfv, BMFace * { BMIter bmiter; BMFace *f; + Ref *r; - if (kfv->isface && facef) { + if (kfv->is_face && facef) { knife_append_list(kcd, lst, facef); } else if (kfv->v) { @@ -654,6 +666,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) @@ -680,7 +697,7 @@ static void knife_cut_through(KnifeTool_OpData *kcd) ListBase firstfaces = {NULL, NULL}, lastfaces = {NULL, NULL}; Ref *r, *r2; KnifeEdge **splitkfe; - int i, j, found; + int i, j; if (!kcd->totlinehit) { /* if no linehits then no interesting back face stuff to do */ @@ -718,15 +735,15 @@ static void knife_cut_through(KnifeTool_OpData *kcd) /* For each face incident to firstv, * find the first following linehit (if any) sharing that face and connect */ for (r = firstfaces.first; r; r = r->next) { + bool found = false; 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) { v2 = splitkfe[j] ? kfe2->v1 : knife_split_edge(kcd, kfe2, lh2->hit, &splitkfe[j]); knife_add_single_cut_through(kcd, firstv, v2, f); - found = 1; + found = true; break; } } @@ -748,16 +765,16 @@ static void knife_cut_through(KnifeTool_OpData *kcd) /* For each face attached to edge for this linehit, * find the first following linehit (if any) sharing that face and connect */ for (r = kfe->faces.first; r; r = r->next) { + bool found = false; 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) { v1 = splitkfe[i] ? kfe->v1 : knife_split_edge(kcd, kfe, lh->hit, &splitkfe[i]); v2 = splitkfe[j] ? kfe2->v1 : knife_split_edge(kcd, kfe2, lh2->hit, &splitkfe[j]); knife_add_single_cut_through(kcd, v1, v2, f); - found = 1; + found = true; break; } } @@ -780,6 +797,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; } @@ -880,7 +898,7 @@ static void knife_finish_cut(KnifeTool_OpData *UNUSED(kcd)) } -static void knifetool_draw_angle_snapping(KnifeTool_OpData *kcd) +static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) { bglMats mats; double u[3], u1[2], u2[2], v1[3], v2[3], dx, dy; @@ -996,7 +1014,7 @@ static void knife_init_colors(KnifeColors *colors) static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) { View3D *v3d = CTX_wm_view3d(C); - KnifeTool_OpData *kcd = arg; + const KnifeTool_OpData *kcd = arg; if (v3d->zbuf) glDisable(GL_DEPTH_TEST); @@ -1052,7 +1070,7 @@ 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; + const float vthresh4_sq = vthresh4 * vthresh4; BMEdgeHit *lh; int i; @@ -1072,12 +1090,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_squared_v2v2(lh->schit, sv1) < vthresh4_squared) { + if (len_squared_v2v2(lh->schit, sv1) < vthresh4_sq) { copy_v3_v3(lh->cagehit, lh->kfe->v1->cageco); glVertex3fv(lh->cagehit); lh->v = lh->kfe->v1; } - else if (len_squared_v2v2(lh->schit, sv2) < vthresh4_squared) { + else if (len_squared_v2v2(lh->schit, sv2) < vthresh4_sq) { copy_v3_v3(lh->cagehit, lh->kfe->v2->cageco); glVertex3fv(lh->cagehit); lh->v = lh->kfe->v2; @@ -1150,7 +1168,7 @@ static float len_v3_tri_side_max(const float v1[3], const float v2[3], const flo const float s2 = len_squared_v3v3(v2, v3); const float s3 = len_squared_v3v3(v3, v1); - return sqrtf(MAX3(s1, s2, s3)); + return sqrtf(max_fff(s1, s2, s3)); } static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree, @@ -1169,7 +1187,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 = (FLT_EPSILON / 2.0f) * len_v3_tri_side_max(v1, v2, v3); - const float depsilon_squared = depsilon * depsilon; + const float depsilon_sq = depsilon * depsilon; copy_v3_v3(cos + 0, v1); copy_v3_v3(cos + 3, v2); @@ -1182,7 +1200,7 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree, for (i = 0; i < tot; i++, result++) { BMLoop *l1; - BMFace *hitf; + BMFace *f_hit; ListBase *lst; Ref *ref; @@ -1203,19 +1221,19 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree, 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) { + if (kcd->curr.vert && len_squared_v3v3(kcd->curr.vert->cageco, p) < depsilon_sq) { continue; } - if (kcd->prev.vert && len_squared_v3v3(kcd->prev.vert->cageco, p) < depsilon_squared) { + if (kcd->prev.vert && len_squared_v3v3(kcd->prev.vert->cageco, p) < depsilon_sq) { continue; } - if (len_squared_v3v3(kcd->prev.cage, p) < depsilon_squared || - len_squared_v3v3(kcd->curr.cage, p) < depsilon_squared) + if (len_squared_v3v3(kcd->prev.cage, p) < depsilon_sq || + len_squared_v3v3(kcd->curr.cage, p) < depsilon_sq) { continue; } if ((kcd->vc.rv3d->rflag & RV3D_CLIPPING) && - ED_view3d_clipping_test(kcd->vc.rv3d, p, TRUE)) + ED_view3d_clipping_test(kcd->vc.rv3d, p, true)) { continue; } @@ -1225,7 +1243,7 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree, mul_m4_v3(kcd->ob->imat, view); if (kcd->cut_through) { - hitf = FALSE; + f_hit = NULL; } else { /* check if this point is visible in the viewport */ @@ -1249,15 +1267,15 @@ static BMEdgeHit *knife_edge_tri_isect(KnifeTool_OpData *kcd, BMBVHTree *bmtree, add_v3_v3(p1, no); /* ray cast */ - hitf = BMBVH_RayCast(bmtree, p1, no, NULL, NULL); + f_hit = BMBVH_RayCast(bmtree, p1, no, NULL, NULL); } /* ok, if visible add the new point */ - if (!hitf && !BLI_smallhash_haskey(ehash, (intptr_t)kfe)) { + if (!f_hit && !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) + if (len_squared_v3v3(p, kcd->curr.co) < depsilon_sq || + len_squared_v3v3(p, kcd->prev.co) < depsilon_sq) { continue; } @@ -1319,12 +1337,12 @@ static void knife_bgl_get_mats(KnifeTool_OpData *UNUSED(kcd), bglMats *mats) //copy_m4_m4(mats->projection, kcd->vc.rv3d->winmat); } -/* Calculate maximum excursion (doubled) from (0,0,0) of mesh */ +/* 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; + BMesh *bm = kcd->em->bm; float max_xyz = 0.0f; int i; @@ -1332,7 +1350,19 @@ static void calc_ortho_extent(KnifeTool_OpData *kcd) for (i = 0; i < 3; i++) max_xyz = max_ff(max_xyz, fabs(v->co[i])); } - kcd->ortho_extent = 2 * max_xyz; + 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 */ @@ -1359,7 +1389,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) knife_project_v3(kcd, v1, s1); knife_project_v3(kcd, v2, s2); - if (len_v2v2(s1, s2) < 1) + if (len_squared_v2v2(s1, s2) < 1) return; /* unproject screen line */ @@ -1379,8 +1409,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) if (kcd->is_ortho) { if (kcd->ortho_extent == 0.0f) calc_ortho_extent(kcd); - limit_dist_v3(v1, v3, kcd->ortho_extent + 10.0f); - limit_dist_v3(v2, v4, kcd->ortho_extent + 10.0f); + 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); @@ -1412,17 +1442,14 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) /* this works but gives numeric problems [#33400] */ #if 0 -static void knife_input_ray_cast(KnifeTool_OpData *kcd, const int mval_i[2], +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 */ ED_view3d_unproject(&mats, r_origin, mval[0], mval[1], 0.0f); @@ -1444,29 +1471,25 @@ static void knife_input_ray_cast(KnifeTool_OpData *kcd, const int mval_i[2], } #endif -static void knife_input_ray_segment(KnifeTool_OpData *kcd, const int mval_i[2], const float ofs, +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; - float mval[2]; knife_bgl_get_mats(kcd, &mats); - mval[0] = (float)mval_i[0]; - mval[1] = (float)mval_i[1]; - /* 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); + 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) +static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float co[3], float cageco[3], bool *is_space) { BMFace *f; float dist = KMAXDIST; @@ -1475,7 +1498,7 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float co[3], float float ray[3]; /* unproject to find view ray */ - knife_input_ray_segment(kcd, kcd->vc.mval, 1.0f, origin, origin_ofs); + 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); @@ -1484,13 +1507,15 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float co[3], float *is_space = !f; if (!f) { - /* try to use backbuffer selection method if ray casting failed */ - f = EDBM_face_find_nearest(&kcd->vc, &dist); + if (kcd->is_interactive) { + /* try to use backbuffer selection method if ray casting failed */ + f = EDBM_face_find_nearest(&kcd->vc, &dist); - /* cheat for now; just put in the origin instead - * of a true coordinate on the face. - * This just puts a point 1.0f infront of the view. */ - add_v3_v3v3(co, origin, ray); + /* cheat for now; just put in the origin instead + * of a true coordinate on the face. + * This just puts a point 1.0f infront of the view. */ + add_v3_v3v3(co, origin, ray); + } } return f; @@ -1498,18 +1523,21 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float co[3], float /* find the 2d screen space density of vertices within a radius. used to scale snapping * distance for picking edges/verts.*/ -static int knife_sample_screen_density(KnifeTool_OpData *kcd, float radius) +static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius) { BMFace *f; - int is_space; + bool is_space; float co[3], cageco[3], sco[3]; + BLI_assert(kcd->is_interactive == true); + f = knife_find_closest_face(kcd, co, cageco, &is_space); if (f && !is_space) { + const float radius_sq = radius * radius; ListBase *lst; Ref *ref; - float dis; + float dis_sq; int c = 0; knife_project_v3(kcd, cageco, sco); @@ -1524,10 +1552,10 @@ static int knife_sample_screen_density(KnifeTool_OpData *kcd, float radius) knife_project_v3(kcd, kfv->cageco, kfv->sco); - dis = len_v2v2(kfv->sco, sco); - if (dis < radius) { + dis_sq = len_squared_v2v2(kfv->sco, sco); + if (dis_sq < radius_sq) { if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) { - if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, TRUE) == 0) { + if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) { c++; } } @@ -1548,7 +1576,14 @@ static int knife_sample_screen_density(KnifeTool_OpData *kcd, float radius) * surrounding mesh (in screen space)*/ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize) { - float density = (float)knife_sample_screen_density(kcd, maxsize * 2.0f); + float density; + + if (kcd->is_interactive) { + density = (float)knife_sample_screen_density(kcd, maxsize * 2.0f); + } + else { + density = 1.0f; + } if (density < 1.0f) density = 1.0f; @@ -1557,7 +1592,7 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize) } /* p is closest point on edge to the mouse cursor */ -static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, int *is_space) +static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space) { BMFace *f; float co[3], cageco[3], sco[3], maxdist = knife_snap_size(kcd, kcd->ethresh); @@ -1599,7 +1634,7 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo interp_v3_v3v3(vec, kfe->v1->cageco, kfe->v2->cageco, lambda); - if (ED_view3d_clipping_test(kcd->vc.rv3d, vec, TRUE) == 0) { + if (ED_view3d_clipping_test(kcd->vc.rv3d, vec, true) == 0) { cure = kfe; curdis = dis; } @@ -1633,8 +1668,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 { @@ -1653,7 +1688,7 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo /* find a vertex near the mouse cursor, if it exists */ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, - int *is_space) + bool *is_space) { BMFace *f; float co[3], cageco[3], sco[3], maxdist = knife_snap_size(kcd, kcd->vthresh); @@ -1669,10 +1704,11 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo kcd->curr.bmface = f; if (f) { + const float maxdist_sq = maxdist * maxdist; ListBase *lst; Ref *ref; KnifeVert *curv = NULL; - float dis, curdis = FLT_MAX; + float dis_sq, curdis_sq = FLT_MAX; knife_project_v3(kcd, cageco, sco); @@ -1686,17 +1722,17 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo knife_project_v3(kcd, kfv->cageco, kfv->sco); - dis = len_v2v2(kfv->sco, sco); - if (dis < curdis && dis < maxdist) { + dis_sq = len_squared_v2v2(kfv->sco, sco); + if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) { - if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, TRUE) == 0) { + if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) { curv = kfv; - curdis = dis; + curdis_sq = dis_sq; } } else { curv = kfv; - curdis = dis; + curdis_sq = dis_sq; } } } @@ -1712,8 +1748,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; @@ -1732,48 +1768,54 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo return NULL; } +/* update both kcd->curr.mval and kcd->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]; } + + copy_v2_v2(kcd->mval, kcd->curr.mval); } /* update active knife edge/vert pointers */ static int knife_update_active(KnifeTool_OpData *kcd) { + knife_pos_data_clear(&kcd->curr); + copy_v2_v2(kcd->curr.mval, kcd->mval); 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 */ @@ -1791,7 +1833,7 @@ static int knife_update_active(KnifeTool_OpData *kcd) float origin[3]; float origin_ofs[3]; - knife_input_ray_segment(kcd, kcd->vc.mval, 1.0f, origin, origin_ofs); + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); closest_to_line_v3(kcd->curr.cage, kcd->prev.cage, origin_ofs, origin); } @@ -1840,7 +1882,7 @@ 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, "geom.out", BM_FACE, FACE_NEW); @@ -1891,7 +1933,7 @@ static void remerge_faces(KnifeTool_OpData *kcd) if (BLI_array_count(faces) > 0) { idx = BM_elem_index_get(faces[0]); - f2 = BM_faces_join(bm, faces, BLI_array_count(faces), TRUE); + f2 = BM_faces_join(bm, faces, BLI_array_count(faces), true); if (f2) { BMO_elem_flag_enable(bm, f2, FACE_NEW); BM_elem_index_set(f2, idx); /* set_dirty! *//* BMESH_TODO, check if this is valid or not */ @@ -1971,14 +2013,14 @@ 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); @@ -2004,13 +2046,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; @@ -2073,7 +2115,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); @@ -2102,7 +2144,7 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd) f2 = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, - NULL, FALSE); + NULL, false); BMO_elem_flag_enable(bm, f2, FACE_NEW); @@ -2142,7 +2184,7 @@ static void knifenet_fill_faces(KnifeTool_OpData *kcd) BM_elem_attrs_copy(bm, bm, f2, f); BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) { - BM_loop_interp_from_face(bm, l1, f2, TRUE, TRUE); + BM_loop_interp_from_face(bm, l1, f2, true, true); } } @@ -2216,15 +2258,15 @@ static void sort_by_frac_along(ListBase *lst, BMEdge *e) /* The chain so far goes from an instantiated vertex to kfv (some may be reversed). * If possible, complete the chain to another instantiated vertex and return 1, else return 0. * The visited hash says which KnifeVert's have already been tried, not including kfv. */ -static int find_chain_search(KnifeTool_OpData *kcd, KnifeVert *kfv, ListBase *fedges, SmallHash *visited, - ListBase *chain) +static bool find_chain_search(KnifeTool_OpData *kcd, KnifeVert *kfv, ListBase *fedges, SmallHash *visited, + ListBase *chain) { Ref *r; KnifeEdge *kfe; KnifeVert *kfv_other; if (kfv->v) - return TRUE; + return true; BLI_smallhash_insert(visited, (uintptr_t)kfv, NULL); /* Try all possible next edges. Could either go through fedges @@ -2241,22 +2283,22 @@ static int find_chain_search(KnifeTool_OpData *kcd, KnifeVert *kfv, ListBase *fe if (kfv_other && !BLI_smallhash_haskey(visited, (uintptr_t)kfv_other)) { knife_append_list(kcd, chain, kfe); if (find_chain_search(kcd, kfv_other, fedges, visited, chain)) - return TRUE; + return true; BLI_remlink(chain, chain->last); } } - return FALSE; + return false; } static ListBase *find_chain_from_vertex(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMVert *v, ListBase *fedges) { SmallHash visited_, *visited = &visited_; ListBase *ans; - int found; + bool found; ans = knife_empty_list(kcd); knife_append_list(kcd, ans, kfe); - found = 0; + found = false; BLI_smallhash_init(visited); if (kfe->v1->v == v) { BLI_smallhash_insert(visited, (uintptr_t)(kfe->v1), NULL); @@ -2317,15 +2359,15 @@ static ListBase *find_chain(KnifeTool_OpData *kcd, ListBase *fedges) /* The hole so far goes from kfvfirst to kfv (some may be reversed). * If possible, complete the hole back to kfvfirst and return 1, else return 0. * The visited hash says which KnifeVert's have already been tried, not including kfv or kfvfirst. */ -static int find_hole_search(KnifeTool_OpData *kcd, KnifeVert *kfvfirst, KnifeVert *kfv, ListBase *fedges, - SmallHash *visited, ListBase *hole) +static bool find_hole_search(KnifeTool_OpData *kcd, KnifeVert *kfvfirst, KnifeVert *kfv, ListBase *fedges, + SmallHash *visited, ListBase *hole) { Ref *r; KnifeEdge *kfe, *kfelast; KnifeVert *kfv_other; if (kfv == kfvfirst) - return TRUE; + return true; BLI_smallhash_insert(visited, (uintptr_t)kfv, NULL); kfelast = ((Ref *)hole->last)->ref; @@ -2343,11 +2385,11 @@ static int find_hole_search(KnifeTool_OpData *kcd, KnifeVert *kfvfirst, KnifeVer if (kfv_other && !BLI_smallhash_haskey(visited, (uintptr_t)kfv_other)) { knife_append_list(kcd, hole, kfe); if (find_hole_search(kcd, kfvfirst, kfv_other, fedges, visited, hole)) - return TRUE; + return true; BLI_remlink(hole, hole->last); } } - return FALSE; + return false; } /* Find a hole (simple cycle with no instantiated vertices). @@ -2358,10 +2400,10 @@ static ListBase *find_hole(KnifeTool_OpData *kcd, ListBase *fedges) Ref *r, *ref; KnifeEdge *kfe; SmallHash visited_, *visited = &visited_; - int found; + bool found; ans = NULL; - found = FALSE; + found = false; for (r = fedges->first; r && !found; r = r->next) { kfe = r->ref; @@ -2392,11 +2434,11 @@ static ListBase *find_hole(KnifeTool_OpData *kcd, ListBase *fedges) } /* Try to find "nice" diagonals - short, and far apart from each other. - * If found, return TRUE and make a 'main chain' going across f which uses + * If found, return true and make a 'main chain' going across f which uses * the two diagonals and one part of the hole, and a 'side chain' that * completes the hole. */ -static int find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, ListBase **mainchain, - ListBase **sidechain) +static bool find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, ListBase **mainchain, + ListBase **sidechain) { float **fco, **hco; BMVert **fv; @@ -2408,14 +2450,14 @@ 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 = 0 /* Quite warnings */, bestsep; + int nh, nf, i, j, k, m, ax, ay, sep = 0 /* Quite warnings */, bestsep; int besti[2], bestj[2]; float d, bestd; nh = BLI_countlist(hole); nf = f->len; if (nh < 2 || nf < 3) - return 0; + return false; /* Gather 2d projections of hole and face vertex coordinates. * Use best-axis projection - not completely accurate, maybe revisit */ @@ -2476,18 +2518,20 @@ static int find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, Li bestd = FLT_MAX; } for (j = 0; j < nf; j++) { + bool ok; + if (m == 1 && j == bestj[0]) continue; d = len_squared_v2v2(hco[i], fco[j]); if (d > bestd) continue; - ok = TRUE; + ok = true; for (k = 0; k < nh && ok; k++) { if (k == i || (k + 1) % nh == i) continue; if (isect_line_line_v2(hco[i], fco[j], hco[k], hco[(k + 1) % nh])) - ok = FALSE; + ok = false; } if (!ok) continue; @@ -2495,7 +2539,7 @@ static int find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, Li if (k == j || (k + 1) % nf == j) continue; if (isect_line_line_v2(hco[i], fco[j], fco[k], fco[(k + 1) % nf])) - ok = FALSE; + ok = false; } if (ok) { besti[m] = i; @@ -2530,14 +2574,14 @@ static int find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, Li } *sidechain = chain; - return TRUE; + return true; } else { - return FALSE; + return false; } } -static int knife_edge_in_face(KnifeTool_OpData *UNUSED(kcd), KnifeEdge *kfe, BMFace *f) +static bool knife_edge_in_face(KnifeTool_OpData *UNUSED(kcd), KnifeEdge *kfe, BMFace *f) { /* BMesh *bm = kcd->em->bm; */ /* UNUSED */ BMVert *v1, *v2; @@ -2547,7 +2591,7 @@ static int knife_edge_in_face(KnifeTool_OpData *UNUSED(kcd), KnifeEdge *kfe, BMF int v1inside, v2inside; if (!f) - return FALSE; + return false; v1 = kfe->v1->v; v2 = kfe->v2->v; @@ -2566,7 +2610,7 @@ static int knife_edge_in_face(KnifeTool_OpData *UNUSED(kcd), KnifeEdge *kfe, BMF v1inside = l1 ? 0 : BM_face_point_inside_test(f, kfe->v1->co); v2inside = l2 ? 0 : BM_face_point_inside_test(f, kfe->v2->co); if ((l1 && v2inside) || (l2 && v1inside) || (v1inside && v2inside)) - return TRUE; + return true; if (l1 && l2) { /* Can have case where v1 and v2 are on shared chain between two faces. * BM_face_legal_splits does visibility and self-intersection tests, @@ -2575,7 +2619,7 @@ static int knife_edge_in_face(KnifeTool_OpData *UNUSED(kcd), KnifeEdge *kfe, BMF mid_v3_v3v3(mid, kfe->v1->co, kfe->v2->co); return BM_face_point_inside_test(f, mid); } - return FALSE; + return false; } /* Split face f with KnifeEdges on chain. f remains as one side, the face formed is put in *newface. @@ -2616,7 +2660,7 @@ static void knife_make_chain_cut(KnifeTool_OpData *kcd, BMFace *f, ListBase *cha *newface = NULL; } else { - *newface = BM_face_split(bm, f, v1, v2, &lnew, NULL, TRUE); + *newface = BM_face_split(bm, f, v1, v2, &lnew, NULL, true); } } else { @@ -2628,7 +2672,7 @@ static void knife_make_chain_cut(KnifeTool_OpData *kcd, BMFace *f, ListBase *cha for (l_iter = lnew->next, i = 0; i < nco; l_iter = l_iter->next, i++) { BLI_assert(equals_v3v3(cos[i], l_iter->v->co)); if (kcd->select_result) { - BM_edge_select_set(bm, l_iter->e, TRUE); + BM_edge_select_set(bm, l_iter->e, true); } kverts[i]->v = l_iter->v; } @@ -2638,7 +2682,7 @@ static void knife_make_chain_cut(KnifeTool_OpData *kcd, BMFace *f, ListBase *cha /* the select chain above doesnt account for the first loop */ if (kcd->select_result) { if (lnew) { - BM_edge_select_set(bm, lnew->e, TRUE); + BM_edge_select_set(bm, lnew->e, true); } } } @@ -2675,7 +2719,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe if (fnew_kfedges->first) knife_make_face_cuts(kcd, fnew, fnew_kfedges); - /* find_chain should always remove edges if it returns TRUE, + /* find_chain should always remove edges if it returns true, * but guard against infinite loop anyway */ count = BLI_countlist(kfedges); if (count >= oldcount) { @@ -2698,10 +2742,16 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe kfe = ((Ref *)sidechain->first)->ref; if (knife_edge_in_face(kcd, kfe, f)) { knife_make_chain_cut(kcd, f, sidechain, &fnew2); + if (fnew2 == NULL) { + return; + } fhole = f; } else if (knife_edge_in_face(kcd, kfe, fnew)) { knife_make_chain_cut(kcd, fnew, sidechain, &fnew2); + if (fnew2 == NULL) { + return; + } fhole = fnew2; } else { @@ -2735,7 +2785,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe knife_make_face_cuts(kcd, fnew2, fnew2_kfedges); if (f == fhole) break; - /* find_hole should always remove edges if it returns TRUE, + /* find_hole should always remove edges if it returns true, * but guard against infinite loop anyway */ count = BLI_countlist(kfedges); if (count >= oldcount) { @@ -2830,10 +2880,8 @@ 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_ex(KnifeTool_OpData *kcd) { - KnifeTool_OpData *kcd = op->customdata; - #if SCANFILL_CUTS knifenet_fill_faces(kcd); #else @@ -2841,21 +2889,12 @@ 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 */ -static int project_knife_view_clip(View3D *v3d, RegionView3D *rv3d, float *clipsta, float *clipend) +static void knifetool_finish(wmOperator *op) { - int orth = ED_view3d_clip_range_get(v3d, rv3d, clipsta, clipend); - - if (orth) { /* only needed for ortho */ - float fac = 2.0f / ((*clipend) - (*clipsta)); - *clipsta *= fac; - *clipend *= fac; - } - - return orth; + KnifeTool_OpData *kcd = op->customdata; + knifetool_finish_ex(kcd); } static void knife_recalc_projmat(KnifeTool_OpData *kcd) @@ -2864,22 +2903,22 @@ static void knife_recalc_projmat(KnifeTool_OpData *kcd) ED_view3d_ob_project_mat_get(kcd->ar->regiondata, kcd->ob, kcd->projmat); //mult_m4_m4m4(kcd->projmat, kcd->vc.rv3d->winmat, kcd->vc.rv3d->viewmat); - kcd->is_ortho = project_knife_view_clip(kcd->vc.v3d, kcd->vc.rv3d, - &kcd->clipsta, &kcd->clipend); + kcd->is_ortho = ED_view3d_clip_range_get(kcd->vc.v3d, kcd->vc.rv3d, + &kcd->clipsta, &kcd->clipend, true); } /* called when modal loop selection is done... */ -static void knifetool_exit(bContext *C, wmOperator *op) +static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd) { - KnifeTool_OpData *kcd = op->customdata; - if (!kcd) return; - WM_cursor_restore(CTX_wm_window(C)); + if (kcd->is_interactive) { + WM_cursor_restore(CTX_wm_window(C)); - /* deactivate the extra drawing stuff in 3D-View */ - ED_region_draw_cb_exit(kcd->ar->type, kcd->draw_handle); + /* deactivate the extra drawing stuff in 3D-View */ + ED_region_draw_cb_exit(kcd->ar->type, kcd->draw_handle); + } /* free the custom data */ BLI_mempool_destroy(kcd->refs); @@ -2904,6 +2943,11 @@ static void knifetool_exit(bContext *C, wmOperator *op) /* destroy kcd itself */ MEM_freeN(kcd); +} +static void knifetool_exit(bContext *C, wmOperator *op) +{ + KnifeTool_OpData *kcd = op->customdata; + knifetool_exit_ex(C, kcd); op->customdata = NULL; } @@ -2921,35 +2965,36 @@ 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 float mval[2]) { knife_recalc_projmat(kcd); - kcd->vc.mval[0] = mval[0]; - kcd->vc.mval[1] = mval[1]; + copy_v2_v2(kcd->mval, mval); if (knife_update_active(kcd)) { ED_region_tag_redraw(kcd->ar); } } +static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) +{ + float mval[2] = {UNPACK2(mval_i)}; + knifetool_update_mval(kcd, mval); +} + /* called when modal loop selection gets set up... */ -static int knifetool_init(bContext *C, wmOperator *op, int UNUSED(do_cut)) +static void knifetool_init(bContext *C, KnifeTool_OpData *kcd, + const bool only_select, const bool cut_through, const bool is_interactive) { - KnifeTool_OpData *kcd; Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); DerivedMesh *cage, *final; SmallHash shash; void *data[3]; - const short only_select = RNA_boolean_get(op->ptr, "only_selected"); - - /* alloc new customdata */ - kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), "knifetool Modal Op Data"); /* assign the drawing handle for drawing preview line... */ kcd->ob = obedit; kcd->ar = CTX_wm_region(C); - kcd->draw_handle = ED_region_draw_cb_activate(kcd->ar->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW); + em_setup_viewcontext(C, &kcd->vc); kcd->em = BMEdit_FromObject(kcd->ob); @@ -2975,7 +3020,7 @@ static int knifetool_init(bContext *C, wmOperator *op, int UNUSED(do_cut)) kcd->vthresh = KMAXDIST - 1; kcd->ethresh = KMAXDIST; - kcd->extend = 1; + kcd->extend = true; knife_recalc_projmat(kcd); @@ -2990,7 +3035,8 @@ static int knifetool_init(bContext *C, wmOperator *op, int UNUSED(do_cut)) kcd->kedgefacemap = BLI_ghash_ptr_new("knife origvertmap"); /* cut all the way through the mesh if use_occlude_geometry button not pushed */ - kcd->cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry"); + kcd->is_interactive = is_interactive; + kcd->cut_through = cut_through; kcd->only_select = only_select; /* can't usefully select resulting edges in face mode */ @@ -2999,9 +3045,11 @@ static int knifetool_init(bContext *C, wmOperator *op, int UNUSED(do_cut)) knife_pos_data_clear(&kcd->curr); knife_pos_data_clear(&kcd->prev); - knife_init_colors(&kcd->colors); + if (is_interactive) { + kcd->draw_handle = ED_region_draw_cb_activate(kcd->ar->type, knifetool_draw, kcd, REGION_DRAW_POST_VIEW); - return 1; + knife_init_colors(&kcd->colors); + } } static int knifetool_cancel(bContext *C, wmOperator *op) @@ -3011,21 +3059,25 @@ 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) { + const bool only_select = RNA_boolean_get(op->ptr, "only_selected"); + const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry"); + KnifeTool_OpData *kcd; view3d_operator_needs_opengl(C); - if (!knifetool_init(C, op, 0)) - return OPERATOR_CANCELLED; + /* alloc new customdata */ + kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); + + knifetool_init(C, kcd, only_select, cut_through, true); /* add a modal handler for this operator - handles loop selection */ WM_cursor_modal(CTX_wm_window(C), BC_KNIFECURSOR); WM_event_add_modal_handler(C, op); - kcd = op->customdata; - knifetool_update_mval(kcd, evt->mval); + knifetool_update_mval_i(kcd, event->mval); knife_update_header(C, kcd); @@ -3096,11 +3148,11 @@ 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 = CTX_data_edit_object(C); KnifeTool_OpData *kcd = op->customdata; - int do_refresh = FALSE; + bool do_refresh = false; if (!obedit || obedit->type != OB_MESH || BMEdit_FromObject(obedit) != kcd->em) { knifetool_exit(C, op); @@ -3129,50 +3181,50 @@ 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); return OPERATOR_FINISHED; case KNF_MODAL_MIDPOINT_ON: - kcd->snap_midpoints = 1; + kcd->snap_midpoints = true; knife_recalc_projmat(kcd); knife_update_active(kcd); knife_update_header(C, kcd); ED_region_tag_redraw(kcd->ar); - do_refresh = TRUE; + do_refresh = true; break; case KNF_MODAL_MIDPOINT_OFF: - kcd->snap_midpoints = 0; + kcd->snap_midpoints = false; knife_recalc_projmat(kcd); knife_update_active(kcd); knife_update_header(C, kcd); ED_region_tag_redraw(kcd->ar); - do_refresh = TRUE; + 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; + kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true; knife_update_header(C, kcd); - do_refresh = TRUE; + 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; + kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false; knife_update_header(C, kcd); - do_refresh = TRUE; + do_refresh = true; break; case KNF_MODAL_ANGLE_SNAP_TOGGLE: kcd->angle_snapping = !kcd->angle_snapping; knife_update_header(C, kcd); - do_refresh = TRUE; + do_refresh = true; break; case KNF_MODAL_CUT_THROUGH_TOGGLE: kcd->cut_through = !kcd->cut_through; knife_update_header(C, kcd); - do_refresh = TRUE; + do_refresh = true; break; case KNF_MODAL_NEW_CUT: ED_region_tag_redraw(kcd->ar); @@ -3218,7 +3270,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, wmEvent *event) case MOUSEMOVE: /* mouse moved somewhere to select another loop */ if (kcd->mode != MODE_PANNING) { - knifetool_update_mval(kcd, event->mval); + knifetool_update_mval_i(kcd, event->mval); } break; @@ -3228,7 +3280,7 @@ 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); + knifetool_update_mval_i(kcd, event->mval); } /* keep going until the user confirms */ @@ -3251,6 +3303,232 @@ void MESH_OT_knife_tool(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - RNA_def_boolean(ot->srna, "use_occlude_geometry", TRUE, "Occlude Geometry", "Only cut the front most geometry"); - RNA_def_boolean(ot->srna, "only_selected", FALSE, "Only Selected", "Only cut selected geometry"); + RNA_def_boolean(ot->srna, "use_occlude_geometry", true, "Occlude Geometry", "Only cut the front most geometry"); + RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry"); +} + + +/* -------------------------------------------------------------------- */ +/* Knife tool as a utility function + * that can be used for internal slicing operations */ + +/** + * Return a point inside the face. + * + * tessellation here seems way overkill, + * but without this its very hard to know of a point is inside the face + */ +static void edvm_mesh_knife_face_point(BMFace *f, float r_cent[3]) +{ + int tottri = f->len - 2; + BMLoop **loops = BLI_array_alloca(loops, f->len); + int (*index)[3] = BLI_array_alloca(index, tottri); + int j; + + float const *best_co[3] = {NULL}; + float best_area = -1.0f; + bool ok = false; + + tottri = BM_face_calc_tessellation(f, loops, index); + BLI_assert(tottri <= f->len - 2); + + for (j = 0; j < tottri; j++) { + const float *p1 = loops[index[j][0]]->v->co; + const float *p2 = loops[index[j][1]]->v->co; + const float *p3 = loops[index[j][2]]->v->co; + float area; + + float cross[3]; + cross_v3_v3v3(cross, p2, p3); + area = fabsf(dot_v3v3(p1, cross)); + if (area > best_area) { + best_co[0] = p1; + best_co[1] = p2; + best_co[2] = p3; + best_area = area; + ok = true; + } + } + + if (ok) { + mid_v3_v3v3v3(r_cent, best_co[0], best_co[1], best_co[2]); + } + else { + mid_v3_v3v3v3(r_cent, loops[0]->v->co, loops[1]->v->co, loops[2]->v->co); + } +} + +static bool edbm_mesh_knife_face_isect(ARegion *ar, LinkNode *polys, BMFace *f, float projmat[4][4]) +{ + float cent_ss[2]; + float cent[3]; + + edvm_mesh_knife_face_point(f, cent); + + ED_view3d_project_float_v2_m4(ar, cent, cent_ss, projmat); + + /* check */ + { + LinkNode *p = polys; + int isect = 0; + + while (p) { + const float (*mval_fl)[2] = p->link; + const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl); + isect += (int)isect_point_poly_v2(cent_ss, mval_fl, mval_tot - 1); + p = p->next; + } + + if (isect % 2) { + return true; + } + } + + return false; +} + +/** + * \param use_tag When set, tag all faces inside the polylines. + */ +void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag) +{ + KnifeTool_OpData *kcd; + + view3d_operator_needs_opengl(C); + + /* init */ + { + const bool only_select = false; + const bool cut_through = false; + const bool is_interactive = false; /* can enable for testing */ + + kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); + + knifetool_init(C, kcd, only_select, cut_through, is_interactive); + + kcd->ignore_edge_snapping = true; + kcd->ignore_vert_snapping = true; + + if (use_tag) { + BM_mesh_elem_hflag_enable_all(kcd->em->bm, BM_EDGE, BM_ELEM_TAG, false); + } + } + + /* execute */ + { + LinkNode *p = polys; + + knife_recalc_projmat(kcd); + + while (p) { + const float (*mval_fl)[2] = p->link; + const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl); + int i; + + for (i = 0; i < mval_tot; i++) { + knifetool_update_mval(kcd, mval_fl[i]); + if (i == 0) { + knife_start_cut(kcd); + kcd->mode = MODE_DRAGGING; + } + else { + knife_add_cut(kcd); + } + } + knife_finish_cut(kcd); + kcd->mode = MODE_IDLE; + p = p->next; + } + } + + /* finish */ + { + knifetool_finish_ex(kcd); + + /* tag faces inside! */ + if (use_tag) { + BMesh *bm = kcd->em->bm; + float projmat[4][4]; + + BMEdge *e; + BMIter iter; + + bool keep_search; + + ED_view3d_ob_project_mat_get(kcd->ar->regiondata, kcd->ob, projmat); + + /* use face-loop tag to store if we have intersected */ +#define F_ISECT_IS_UNKNOWN(f) BM_elem_flag_test(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) +#define F_ISECT_SET_UNKNOWN(f) BM_elem_flag_enable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) +#define F_ISECT_SET_OUTSIDE(f) BM_elem_flag_disable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) + { + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + F_ISECT_SET_UNKNOWN(f); + BM_elem_flag_disable(f, BM_ELEM_TAG); + } + } + + /* tag all faces linked to cut edges */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + /* check are we tagged?, then we are an original face */ + if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { + BMFace *f; + BMIter fiter; + BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { + if (edbm_mesh_knife_face_isect(kcd->ar, polys, f, projmat)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + } + } + } + } + + /* expand tags for faces which are not cut, but are inside the polys */ + do { + BMFace *f; + keep_search = false; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { + /* am I connected to a tagged face via an un-tagged edge (ie, not across a cut) */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + bool found = false; + + do { + if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { + /* now check if the adjacent faces is tagged */ + BMLoop *l_radial_iter = l_iter->radial_next; + if (l_radial_iter != l_iter) { + do { + if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + found = true; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && (found == false)); + } + } + } while ((l_iter = l_iter->next) != l_first && (found == false)); + + if (found) { + if (edbm_mesh_knife_face_isect(kcd->ar, polys, f, projmat)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + keep_search = true; + } + else { + /* don't loose time on this face again, set it as outside */ + F_ISECT_SET_OUTSIDE(f); + } + } + } + } + } while (keep_search); + +#undef F_ISECT_IS_UNKNOWN +#undef F_ISECT_SET_UNKNOWN +#undef F_ISECT_SET_OUTSIDE + + } + + knifetool_exit_ex(C, kcd); + kcd = NULL; + } } |