Cleanup: miscellaneous improvements in knife code

- Remove unused members.
- Use C++ vector types.
- Rename variables.
- Avoid redundant calls.
- Pass the modified value as a function return parameter.
This commit is contained in:
Germano Cavalcante
2024-02-21 14:08:54 -03:00
parent 4e544e5e59
commit 6d491da0be

View File

@@ -116,7 +116,6 @@ struct KnifeVert {
int ob_index;
float co[3], cageco[3];
bool is_face, in_space;
bool is_cut; /* Along a cut created by user input (will draw too). */
bool is_invalid;
bool is_splitting; /* Created when an edge was split. */
@@ -165,7 +164,7 @@ struct KnifePosData {
* -1 represents the absence of an object. */
int ob_index;
float mval[2]; /* Mouse screen position (may be non-integral if snapped to something). */
float2 mval; /* Mouse screen position (may be non-integral if snapped to something). */
bool is_space() const
{
@@ -1837,20 +1836,15 @@ static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
/** \name Cut/Hit Utils
* \{ */
static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval);
/* User has just clicked for first time or first time after a restart (E key).
* Copy the current position data into prev. */
static void knife_start_cut(KnifeTool_OpData *kcd)
static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
{
knife_snap_curr(kcd, mval);
kcd->prev = kcd->curr;
kcd->mdata.is_stored = false;
if (kcd->prev.vert == nullptr && kcd->prev.edge == nullptr) {
float ofs_local[3];
negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs);
ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, ofs_local, kcd->curr.mval, kcd->prev.cage);
copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
}
}
static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh)
@@ -2064,7 +2058,6 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd,
kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit);
kfe->v1->ob_index = lh1->ob_index;
kfe->v1->is_cut = true;
kfe->v1->is_face = true;
knife_append_list(kcd, &kfe->v1->faces, lh1->f);
lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
}
@@ -2081,7 +2074,6 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd,
kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit);
kfe->v2->ob_index = lh2->ob_index;
kfe->v2->is_cut = true;
kfe->v2->is_face = true;
knife_append_list(kcd, &kfe->v2->faces, lh2->f);
lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */
}
@@ -2830,7 +2822,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
KnifeLineHit *linehits = nullptr;
BLI_array_declare(linehits);
KnifeLineHit hit;
float s[2], se1[2], se2[2], sint[2];
float s[2], se1[2], se2[2];
float d1, d2;
float vert_tol, vert_tol_sq;
float line_tol, line_tol_sq;
@@ -3016,55 +3008,66 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
knife_project_v2(kcd, kfe->v1->cageco, se1);
knife_project_v2(kcd, kfe->v2->cageco, se2);
int isect_kind = 1;
float3 p_cage;
float2 p_cage_ss;
bool kfe_is_in_cut = false;
if (kfe == kcd->prev.edge) {
/* This KnifeEdge was captured by the snap system. */
copy_v2_v2(sint, kcd->prev.mval);
p_cage = kcd->prev.cage;
p_cage_ss = kcd->prev.mval;
kfe_is_in_cut = true;
}
else if (kfe == kcd->curr.edge) {
/* This KnifeEdge was captured by the snap system. */
copy_v2_v2(sint, kcd->curr.mval);
p_cage = kcd->curr.cage;
p_cage_ss = kcd->curr.mval;
kfe_is_in_cut = true;
}
else {
isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, sint);
int isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, p_cage_ss);
if (isect_kind == -1) {
/* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */
closest_to_line_segment_v2(sint, s1, se1, se2);
if (len_squared_v2v2(sint, s1) <= line_tol_sq) {
closest_to_line_segment_v2(p_cage_ss, s1, se1, se2);
if (len_squared_v2v2(p_cage_ss, s1) <= line_tol_sq) {
isect_kind = 1;
}
else {
closest_to_line_segment_v2(sint, s2, se1, se2);
if (len_squared_v2v2(sint, s2) <= line_tol_sq) {
closest_to_line_segment_v2(p_cage_ss, s2, se1, se2);
if (len_squared_v2v2(p_cage_ss, s2) <= line_tol_sq) {
isect_kind = 1;
}
}
}
}
if (isect_kind == 1) {
d1 = len_v2v2(sint, se1);
d2 = len_v2v2(se2, se1);
if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
float3 r1, r2;
float p_cage[3], p_cage_tmp[3];
/* Can't just interpolate between ends of kfe because
* that doesn't work with perspective transformation.
* Need to find 3d intersection of ray through sint. */
knife_input_ray_segment(kcd, sint, r1, r2);
if (isect_kind == 1) {
d1 = len_v2v2(p_cage_ss, se1);
d2 = len_v2v2(se2, se1);
if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
float3 r1, r2;
float3 p_cage_dummy;
/* Can't just interpolate between ends of kfe because
* that doesn't work with perspective transformation.
* Need to find 3d intersection of ray through sint. */
knife_input_ray_segment(kcd, p_cage_ss, r1, r2);
isect_kind = isect_line_line_v3(
kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_tmp);
if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, bm_elem_from_knife_edge(kfe))) {
if (kcd->snap_midpoints) {
/* Choose intermediate point snap too. */
mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
mid_v2_v2v2(sint, se1, se2);
isect_kind = isect_line_line_v3(
kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_dummy);
if (isect_kind >= 1 &&
point_is_visible(kcd, p_cage, p_cage_ss, bm_elem_from_knife_edge(kfe)))
{
if (kcd->snap_midpoints) {
/* Choose intermediate point snap too. */
mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
mid_v2_v2v2(p_cage_ss, se1, se2);
}
kfe_is_in_cut = true;
}
knife_linehit_set(kcd, s1, s2, sint, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
BLI_array_append(linehits, hit);
}
}
}
if (kfe_is_in_cut) {
knife_linehit_set(kcd, s1, s2, p_cage_ss, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
BLI_array_append(linehits, hit);
}
}
/* Now face hits; don't add if a vertex or edge in face should have hit. */
@@ -3106,6 +3109,7 @@ static void knife_pos_data_clear(KnifePosData *kpd)
kpd->vert = nullptr;
kpd->edge = nullptr;
kpd->bmface = nullptr;
kpd->ob_index = -1;
zero_v2(kpd->mval);
}
@@ -3115,21 +3119,23 @@ static void knife_pos_data_clear(KnifePosData *kpd)
/** \name Snapping (#knife_snap_update_from_mval)
* \{ */
static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, int *r_ob_index, float3 &r_cageco)
static bool knife_find_closest_face(KnifeTool_OpData *kcd, const float2 &mval, KnifePosData *r_kpd)
{
float3 cage;
int ob_index;
BMFace *f;
float dist = KMAXDIST;
float3 origin;
float3 ray_normal;
ED_view3d_win_to_ray_clipped(
kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, kcd->curr.mval, origin, ray_normal, false);
kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, origin, ray_normal, false);
f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, r_cageco, r_ob_index);
f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, cage, &ob_index);
if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) {
f = nullptr;
*r_ob_index = -1;
ob_index = -1;
}
if (f == nullptr) {
@@ -3139,24 +3145,32 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, int *r_ob_index, f
* Apply the mouse coordinates to a copy of the view-context
* since we don't want to rely on this being set elsewhere. */
ViewContext vc = kcd->vc;
vc.mval[0] = int(kcd->curr.mval[0]);
vc.mval[1] = int(kcd->curr.mval[1]);
vc.mval[0] = int(mval[0]);
vc.mval[1] = int(mval[1]);
f = EDBM_face_find_nearest(&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 in front of the view. */
r_cageco = origin + ray_normal;
if (f) {
*r_ob_index = 0;
BLI_assert(*r_ob_index == kcd->objects.first_index_of_try(vc.obact));
/* Cheat for now; just put in the origin instead
* of a true coordinate on the face.
* This just puts a point 1.0f in front of the view. */
cage = origin + ray_normal;
ob_index = 0;
BLI_assert(ob_index == kcd->objects.first_index_of_try(vc.obact));
}
}
}
return f;
if (f) {
r_kpd->cage = cage;
r_kpd->bmface = f;
r_kpd->ob_index = ob_index;
r_kpd->mval = mval;
return true;
}
return false;
}
/**
@@ -3238,19 +3252,19 @@ static bool knife_snap_edge_constrained(KnifeTool_OpData *kcd,
const float kfv1_sco[2],
const float kfv2_sco[2],
float *r_dist_sq,
float *r_lambda)
float *r_lambda,
float2 &r_sco)
{
/* If snapping, check we're in bounds. */
float sco_snap[2];
isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
float lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, r_sco);
float lambda = line_point_factor_v2(r_sco, kfv1_sco, kfv2_sco);
/* Be strict when constrained within edge. */
if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
return false;
}
float dis_sq = len_squared_v2v2(sco, sco_snap);
float dis_sq = len_squared_v2v2(sco, r_sco);
if (dis_sq < *r_dist_sq) {
*r_dist_sq = dis_sq;
*r_lambda = lambda;
@@ -3283,12 +3297,9 @@ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd,
}
/* p is closest point on edge to the mouse cursor. */
static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
int ob_index,
BMFace *f,
float cagep[3])
static bool knife_find_closest_edge_of_face(
KnifeTool_OpData *kcd, int ob_index, BMFace *f, const float2 &cage_ss, KnifePosData *r_kpd)
{
float sco[2];
float maxdist;
if (kcd->is_interactive) {
@@ -3303,15 +3314,11 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
}
const float maxdist_sq = maxdist * maxdist;
KnifeEdge *cure = nullptr;
float cur_cagep[3];
ListBase *list;
float dis_sq, curdis_sq = maxdist_sq;
knife_project_v2(kcd, cagep, sco);
float cur_dist_sq = maxdist_sq;
bool has_hit = false;
/* Look through all edges associated with this face. */
list = knife_get_face_kedges(kcd, ob_index, f);
ListBase *list = knife_get_face_kedges(kcd, ob_index, f);
LISTBASE_FOREACH (LinkData *, ref, list) {
KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
float kfv1_sco[2], kfv2_sco[2], test_cagep[3];
@@ -3327,18 +3334,23 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
/* Check if we're close enough and calculate 'lambda'. */
/* In constrained mode calculate lambda differently, unless constrained along kcd->prev.edge */
float2 closest_ss;
float dis_sq;
if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) &&
(kcd->mode == MODE_DRAGGING))
{
dis_sq = curdis_sq;
if (!knife_snap_edge_constrained(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) {
dis_sq = cur_dist_sq;
if (!knife_snap_edge_constrained(
kcd, cage_ss, kfv1_sco, kfv2_sco, &dis_sq, &lambda, closest_ss))
{
continue;
}
}
else {
dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco);
if (dis_sq < curdis_sq) {
lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco);
closest_to_line_segment_v2(closest_ss, cage_ss, kfv1_sco, kfv2_sco);
dis_sq = len_squared_v2v2(closest_ss, cage_ss);
if (dis_sq < cur_dist_sq) {
lambda = line_point_factor_v2(cage_ss, kfv1_sco, kfv2_sco);
}
else {
continue;
@@ -3355,41 +3367,30 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
}
}
cure = kfe;
curdis_sq = dis_sq;
copy_v3_v3(cur_cagep, test_cagep);
}
if (cure && !kcd->ignore_edge_snapping) {
KnifeVert *edgesnap = nullptr;
float3 p;
cur_dist_sq = dis_sq;
r_kpd->edge = kfe;
if (kcd->snap_midpoints) {
mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
mid_v3_v3v3(r_kpd->cage, kfe->v1->cageco, kfe->v2->cageco);
mid_v2_v2v2(r_kpd->mval, kfv1_sco, kfv2_sco);
}
else {
float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
copy_v3_v3(cagep, cur_cagep);
interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
copy_v3_v3(r_kpd->cage, test_cagep);
r_kpd->mval = closest_ss;
}
/* 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);
edgesnap->ob_index = ob_index;
knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
has_hit = true;
}
return cure;
return has_hit;
}
/* Find a vertex near the mouse cursor, if it exists. */
static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
KnifeEdge *kfe,
float cagep[3])
static bool knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
const KnifeEdge *kfe,
const float2 &cage_ss,
KnifePosData *r_kpd)
{
float sco[2];
float maxdist;
if (kcd->is_interactive) {
@@ -3407,8 +3408,6 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
float cur_kfv_sco[2];
float dis_sq, curdis_sq = FLT_MAX;
knife_project_v2(kcd, cagep, sco);
for (int i = 0; i < 2; i++) {
KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
float kfv_sco[2];
@@ -3425,7 +3424,7 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
}
}
dis_sq = len_squared_v2v2(kfv_sco, sco);
dis_sq = len_squared_v2v2(kfv_sco, cage_ss);
if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
!ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false))
@@ -3437,15 +3436,17 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd,
}
}
if (curv && !kcd->ignore_vert_snapping) {
copy_v3_v3(cagep, curv->cageco);
if (curv) {
r_kpd->cage = curv->cageco;
/* Update mouse coordinates to the snapped-to vertex's screen coordinates
* this is important for angle snap, which uses the previous mouse position. */
copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
r_kpd->mval = cur_kfv_sco;
return true;
}
return curv;
return false;
}
/**
@@ -3765,6 +3766,51 @@ static void knife_constrain_axis(KnifeTool_OpData *kcd)
copy_v2_v2(kcd->mval, kcd->curr.mval);
}
static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval)
{
knife_pos_data_clear(&kcd->curr);
if (knife_find_closest_face(kcd, mval, &kcd->curr)) {
if (!kcd->ignore_edge_snapping || !kcd->ignore_vert_snapping) {
KnifePosData kpos_tmp = kcd->curr;
if (knife_find_closest_edge_of_face(
kcd, kpos_tmp.ob_index, kpos_tmp.bmface, kpos_tmp.mval, &kpos_tmp))
{
if (!kcd->ignore_edge_snapping) {
kcd->curr = kpos_tmp;
}
if (!kcd->ignore_vert_snapping) {
knife_find_closest_vert_of_edge(kcd, kpos_tmp.edge, kpos_tmp.mval, &kcd->curr);
}
}
}
}
if (kcd->curr.vert || kcd->curr.edge || kcd->curr.bmface) {
return;
}
/* If no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
* Note that drawing lines in `free-space` isn't properly supported
* but there's no guarantee (0, 0, 0) has any geometry either - campbell */
float origin[3];
float origin_ofs[3];
copy_v2_v2(kcd->curr.mval, mval);
knife_input_ray_segment(kcd, kcd->curr.mval, origin, origin_ofs);
if (!isect_line_plane_v3(
kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
{
copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
/* Should never fail! */
BLI_assert(0);
}
}
/**
* \return true when `kcd->curr.co` & `kcd->curr.cage` are set.
*
@@ -3773,7 +3819,7 @@ static void knife_constrain_axis(KnifeTool_OpData *kcd)
* In this case the selection-buffer is used to select the face,
* then the closest `vert` or `edge` is set, and those will enable `is_co_set`.
*/
static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
copy_v2_v2(kcd->curr.mval, mval);
@@ -3800,25 +3846,7 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[
}
}
{
kcd->curr.ob_index = 0; /* Start with a valid object. */
kcd->curr.bmface = knife_find_closest_face(kcd, &kcd->curr.ob_index, kcd->curr.cage);
if (kcd->curr.bmface) {
kcd->curr.edge = knife_find_closest_edge_of_face(
kcd, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.cage);
}
if (kcd->curr.edge) {
kcd->curr.vert = knife_find_closest_vert_of_edge(kcd, kcd->curr.edge, kcd->curr.cage);
if (kcd->ignore_edge_snapping) {
kcd->curr.edge = nullptr;
}
}
}
return kcd->curr.vert || kcd->curr.edge || (kcd->curr.bmface && !kcd->curr.is_space());
knife_snap_curr(kcd, mval);
}
/**
@@ -4139,25 +4167,7 @@ static void knifetool_exit(wmOperator *op)
/** Update active knife edge/vert pointers. */
static int knife_update_active(KnifeTool_OpData *kcd)
{
/* If no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
* Note that drawing lines in `free-space` isn't properly supported
* but there's no guarantee (0, 0, 0) has any geometry either - campbell */
if (!knife_snap_update_from_mval(kcd, kcd->mval)) {
float origin[3];
float origin_ofs[3];
knife_input_ray_segment(kcd, kcd->curr.mval, origin, origin_ofs);
if (!isect_line_plane_v3(
kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
{
copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
/* Should never fail! */
BLI_assert(0);
}
}
knife_snap_update_from_mval(kcd, kcd->mval);
if (kcd->mode == MODE_DRAGGING) {
knife_find_line_hits(kcd);
@@ -4502,7 +4512,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
knife_add_cut(kcd);
}
else if (kcd->mode != MODE_PANNING) {
knife_start_cut(kcd);
knife_start_cut(kcd, float2(event->mval));
kcd->mode = MODE_DRAGGING;
kcd->init = kcd->curr;
}
@@ -4719,8 +4729,6 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE);
WM_event_add_modal_handler(C, op);
knifetool_update_mval_i(kcd, event->mval);
if (wait_for_input == false) {
/* Avoid copy-paste logic. */
wmEvent event_modal{};
@@ -4728,6 +4736,8 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
event_modal.type = EVT_MODAL_MAP;
event_modal.val = KNF_MODAL_ADD_CUT;
copy_v2_v2_int(event_modal.mval, event->mval);
int ret = knifetool_modal(C, op, &event_modal);
BLI_assert(ret == OPERATOR_RUNNING_MODAL);
UNUSED_VARS_NDEBUG(ret);
@@ -4880,16 +4890,14 @@ void EDBM_mesh_knife(
const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
int i;
for (i = 0; i < mval_tot; i++) {
knife_start_cut(kcd, mval_fl[0]);
kcd->mode = MODE_DRAGGING;
for (i = 1; 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_add_cut(kcd);
}
knife_finish_cut(kcd);
kcd->mode = MODE_IDLE;
p = p->next;