From 86c29afc3f5d59aa10867aac6af2cd7d38abe253 Mon Sep 17 00:00:00 2001 From: acspike Date: Fri, 14 Apr 2006 21:24:32 +0000 Subject: [PATCH] Attempt to preserve the shape of the path when deleting nodes Old deletion behavior is available via Ctrl+Delete --- src/display/bezier-utils.cpp | 3 +- src/display/bezier-utils.h | 1 + src/node-context.cpp | 7 +- src/nodepath.cpp | 145 ++++++++++++++++++++++++++++++----- src/nodepath.h | 1 + 5 files changed, 135 insertions(+), 22 deletions(-) diff --git a/src/display/bezier-utils.cpp b/src/display/bezier-utils.cpp index 8316c86cc..04a38c236 100644 --- a/src/display/bezier-utils.cpp +++ b/src/display/bezier-utils.cpp @@ -53,7 +53,6 @@ static void estimate_bi(NR::Point b[4], unsigned ei, NR::Point const data[], double const u[], unsigned len); static void reparameterize(NR::Point const d[], unsigned len, double u[], BezierCurve const bezCurve); static gdouble NewtonRaphsonRootFind(BezierCurve const Q, NR::Point const &P, gdouble u); -static NR::Point bezier_pt(unsigned degree, NR::Point const V[], gdouble t); static NR::Point sp_darray_center_tangent(NR::Point const d[], unsigned center, unsigned length); static NR::Point sp_darray_right_tangent(NR::Point const d[], unsigned const len); static unsigned copy_without_nans_or_adjacent_duplicates(NR::Point const src[], unsigned src_len, NR::Point dest[]); @@ -652,7 +651,7 @@ NewtonRaphsonRootFind(BezierCurve const Q, NR::Point const &P, gdouble const u) * is i * BezierII(i-1, V'), where for all j, V'[j] = * V[j + 1] - V[j]. */ -static NR::Point +NR::Point bezier_pt(unsigned const degree, NR::Point const V[], gdouble const t) { /** Pascal's triangle. */ diff --git a/src/display/bezier-utils.h b/src/display/bezier-utils.h index f281ab220..1d6c8678f 100644 --- a/src/display/bezier-utils.h +++ b/src/display/bezier-utils.h @@ -20,6 +20,7 @@ #include /* Bezier approximation utils */ +NR::Point bezier_pt(unsigned degree, NR::Point const V[], gdouble t); gint sp_bezier_fit_cubic(NR::Point bezier[], NR::Point const data[], gint len, gdouble error); diff --git a/src/node-context.cpp b/src/node-context.cpp index 1e01b1af9..2e5fd3e73 100644 --- a/src/node-context.cpp +++ b/src/node-context.cpp @@ -636,8 +636,11 @@ sp_node_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_Delete: case GDK_KP_Delete: case GDK_BackSpace: - // with any modifiers - sp_node_selected_delete(); + if (MOD__CTRL_ONLY) { + sp_node_selected_delete(); + } else { + sp_node_delete_preserve(g_list_copy(nc->nodepath->selected)); + } ret = TRUE; break; case GDK_C: diff --git a/src/nodepath.cpp b/src/nodepath.cpp index b50ee90c7..8aed0420b 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -37,9 +37,12 @@ #include "prefs-utils.h" #include "sp-metrics.h" #include "sp-path.h" -#include +#include "libnr/nr-matrix-ops.h" #include "splivarot.h" #include "svg/svg.h" +#include "display/bezier-utils.h" +#include +#include class NR::Matrix; @@ -1614,6 +1617,126 @@ void sp_node_selected_join_segment() sp_nodepath_update_repr(nodepath); } +/** + * Delete one or more selected nodes and preserve the shape of the path as much as possible. + */ +void sp_node_delete_preserve(GList *nodes_to_delete) +{ + + while (nodes_to_delete) { + Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data; + Inkscape::NodePath::SubPath *sp = node->subpath; + Inkscape::NodePath::Path *nodepath = sp->nodepath; + Inkscape::NodePath::Node *sample_cursor = NULL; + Inkscape::NodePath::Node *sample_end = NULL; + Inkscape::NodePath::Node *delete_cursor = node; + bool just_delete = false; + + //find the start of this contiguous selection + //move left to the first node that is not selected + //or the start of the non-closed path + for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) { + delete_cursor = curr; + } + + //just delete at the beginning of an open path + if (!delete_cursor->p.other) { + sample_cursor = delete_cursor; + just_delete = true; + } else { + sample_cursor = delete_cursor->p.other; + } + + //calculate points for each segment + int rate = 5; + float period = 1.0 / rate; + std::vector data; + if (!just_delete) { + data.push_back(sample_cursor->pos); + for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) { + //just delete at the end of an open path + if (!sp->closed && curr->n.other == sp->last) { + just_delete = true; + break; + } + + //sample points on the contiguous selected segment + NR::Point *bez; + bez = new NR::Point [4]; + bez[0] = curr->pos; + bez[1] = curr->n.pos; + bez[2] = curr->n.other->p.pos; + bez[3] = curr->n.other->pos; + for (int i=1; in.other->pos); + + sample_end = curr->n.other; + //break if we've come full circle or hit the end of the selection + if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) { + break; + } + } + } + + if (!just_delete) { + //calculate the best fitting single segment and adjust the endpoints + NR::Point *adata; + adata = new NR::Point [data.size()]; + copy(data.begin(), data.end(), adata); + + NR::Point *bez; + bez = new NR::Point [4]; + //would decreasing error create a better fitting approximation? + gdouble error = 1.0; + gint ret; + ret = sp_bezier_fit_cubic (bez, adata, data.size(), error); + + //adjust endpoints + sample_cursor->n.pos = bez[1]; + sample_end->p.pos = bez[2]; + } + + //destroy this contiguous selection + while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) { + Inkscape::NodePath::Node *temp = delete_cursor; + if (delete_cursor->n.other == delete_cursor) { + // delete_cursor->n points to itself, which means this is the last node on a closed subpath + delete_cursor = NULL; + } else { + delete_cursor = delete_cursor->n.other; + } + nodes_to_delete = g_list_remove(nodes_to_delete, temp); + sp_nodepath_node_destroy(temp); + } + + //clean up the nodepath (such as for trivial subpaths) + sp_nodepath_cleanup(nodepath); + + sp_nodepath_update_handles(nodepath); + + // if the entire nodepath is removed, delete the selected object. + if (nodepath->subpaths == NULL || + sp_nodepath_get_node_count(nodepath) < 2) { + SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop); + sp_nodepath_destroy(nodepath); + g_list_free(nodes_to_delete); + nodes_to_delete = NULL; + //is the next line necessary? + sp_selection_delete(); + sp_document_done (document); + return; + } + + sp_nodepath_update_repr(nodepath); + + sp_nodepath_update_statusbar(nodepath); + } +} + /** * Delete one or more selected nodes. */ @@ -2372,23 +2495,9 @@ static void node_clicked(SPKnot *knot, guint state, gpointer data) sp_nodepath_update_statusbar(nodepath); } else { //ctrl+alt+click: delete node - sp_nodepath_node_destroy(n); - //clean up the nodepath (such as for trivial subpaths) - sp_nodepath_cleanup(nodepath); - - // if the entire nodepath is removed, delete the selected object. - if (nodepath->subpaths == NULL || - sp_nodepath_get_node_count(nodepath) < 2) { - SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop); - sp_nodepath_destroy(nodepath); - sp_selection_delete(); - sp_document_done (document); - - } else { - sp_nodepath_update_handles(nodepath); - sp_nodepath_update_repr(nodepath); - sp_nodepath_update_statusbar(nodepath); - } + GList *node_to_delete = NULL; + node_to_delete = g_list_append(node_to_delete, n); + sp_node_delete_preserve(node_to_delete); } } else { diff --git a/src/nodepath.h b/src/nodepath.h index a0f33d4b1..c9315470f 100644 --- a/src/nodepath.h +++ b/src/nodepath.h @@ -257,6 +257,7 @@ void sp_node_selected_break (void); void sp_node_selected_duplicate (void); void sp_node_selected_join (void); void sp_node_selected_join_segment (void); +void sp_node_delete_preserve (GList *nodes_to_delete); void sp_node_selected_delete (void); void sp_node_selected_delete_segment (void); void sp_node_selected_set_type (Inkscape::NodePath::NodeType type); -- 2.30.2