X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fnodepath.cpp;h=f9a615583fa438c8689eaec09dfeabe9a88dc656;hb=aa42d0d7b4bbf2f20b5f3329d00c4a80ad1598ec;hp=49bf78585b698819e5c944c4a388152f20ffc060;hpb=99bc6b3267625b0b2f266fc5c82c799780b002bf;p=inkscape.git diff --git a/src/nodepath.cpp b/src/nodepath.cpp index 49bf78585..f9a615583 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -21,10 +21,10 @@ #include "display/sodipodi-ctrl.h" #include "display/sp-canvas-util.h" #include -#include <2geom/pathvector.h> -#include <2geom/sbasis-to-bezier.h> -#include <2geom/bezier-curve.h> -#include <2geom/hvlinesegment.h> +#include "2geom/pathvector.h" +#include "2geom/sbasis-to-bezier.h" +#include "2geom/bezier-curve.h" +#include "2geom/hvlinesegment.h" #include "helper/units.h" #include "helper/geom.h" #include "knot.h" @@ -42,18 +42,19 @@ #include "selection-chemistry.h" #include "selection.h" #include "xml/repr.h" -#include "prefs-utils.h" +#include "preferences.h" #include "sp-metrics.h" #include "sp-path.h" +#include "sp-text.h" +#include "sp-shape.h" #include "libnr/nr-matrix-ops.h" #include "svg/svg.h" #include "verbs.h" -#include "display/bezier-utils.h" +#include <2geom/bezier-utils.h> #include #include #include #include -#include #include "live_effects/lpeobject.h" #include "live_effects/lpeobject-reference.h" #include "live_effects/effect.h" @@ -63,7 +64,7 @@ #include "display/snap-indicator.h" #include "snapped-point.h" -class NR::Matrix; +namespace Geom { class Matrix; } /// \todo /// evil evil evil. FIXME: conflict of two different Path classes! @@ -121,19 +122,20 @@ static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::N /* Adjust handle placement, if the node or the other handle is moved */ static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust); static void sp_node_adjust_handles(Inkscape::NodePath::Node *node); +static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node); /* Node event callbacks */ static void node_clicked(SPKnot *knot, guint state, gpointer data); static void node_grabbed(SPKnot *knot, guint state, gpointer data); static void node_ungrabbed(SPKnot *knot, guint state, gpointer data); -static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data); +static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data); /* Handle event callbacks */ static void node_handle_clicked(SPKnot *knot, guint state, gpointer data); static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data); static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data); -static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data); -static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data); +static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data); +static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data); static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n); /* Constructors and destructors */ @@ -143,7 +145,7 @@ static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath); static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp); static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n); static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code, - NR::Point *ppos, NR::Point *pos, NR::Point *npos); + Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos); static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node); /* Helpers */ @@ -159,11 +161,11 @@ static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL; static SPCanvasItem * -sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) { +sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false, guint32 color = 0xff0000ff) { SPCurve *helper_curve = curve->copy(); helper_curve->transform(np->i2d); SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO); sp_canvas_item_move_to_z(helper_path, 0); if (show) { @@ -173,15 +175,9 @@ sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, return helper_path; } -static SPCanvasItem * -canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) { - SPCurve *helper_curve = new SPCurve(pathv); - return sp_nodepath_make_helper_item(np, helper_curve, show); -} - static void sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) { - //std::map >* helper_path_vec; + //std::map > helper_path_vec; if (!SP_IS_LPE_ITEM(np->item)) { g_print ("Only LPEItems can have helperpaths!\n"); return; @@ -191,18 +187,23 @@ sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) { PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem); for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) { Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i); - Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe; - // create new canvas items from the effect's helper paths - std::vector hpaths = lpe->getHelperPaths(lpeitem); - for (std::vector::iterator j = hpaths.begin(); j != hpaths.end(); ++j) { - (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true)); + Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe(); + if (lpe) { + // create new canvas items from the effect's helper paths + std::vector hpaths = lpe->getHelperPaths(lpeitem); + for (std::vector::iterator j = hpaths.begin(); j != hpaths.end(); ++j) { + SPCurve *helper_curve = new SPCurve(*j); + SPCanvasItem * canvasitem = sp_nodepath_make_helper_item(np, helper_curve, true, 0x509050dd); + np->helper_path_vec[lpe].push_back(canvasitem); + helper_curve->unref(); + } } } } void sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) { - //std::map >* helper_path_vec; + //std::map > helper_path_vec; if (!SP_IS_LPE_ITEM(np->item)) { g_print ("Only LPEItems can have helperpaths!\n"); return; @@ -211,38 +212,49 @@ sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) { SPLPEItem *lpeitem = SP_LPE_ITEM(np->item); PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem); for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) { - Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe; - /* update canvas items from the effect's helper paths; note that this code relies on the - * fact that getHelperPaths() will always return the same number of helperpaths in the same - * order as during their creation in sp_nodepath_create_helperpaths - */ - std::vector hpaths = lpe->getHelperPaths(lpeitem); - for (unsigned int j = 0; j < hpaths.size(); ++j) { - SPCurve *curve = new SPCurve(hpaths[j]); - curve->transform(np->i2d); - sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve); - curve = curve->unref(); + Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe(); + if (lpe) { + /* update canvas items from the effect's helper paths; note that this code relies on the + * fact that getHelperPaths() will always return the same number of helperpaths in the same + * order as during their creation in sp_nodepath_create_helperpaths + */ + std::vector hpaths = lpe->getHelperPaths(lpeitem); + for (unsigned int j = 0; j < hpaths.size(); ++j) { + SPCurve *curve = new SPCurve(hpaths[j]); + curve->transform(np->i2d); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve); + curve = curve->unref(); + } } } } static void sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) { - for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) { + for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) { for (std::vector::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { GtkObject *temp = *j; *j = NULL; gtk_object_destroy(temp); } } + np->helper_path_vec.clear(); } /** * \brief Creates new nodepath from item + * + * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject ! + * + * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor. */ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item) { + if (repr_key_in) { + g_assert(IS_LIVEPATHEFFECT(object)); + } + Inkscape::XML::Node *repr = object->repr; /** \todo @@ -274,12 +286,14 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, } //Create new nodepath - Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1); + Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path(); if (!np) { curve->unref(); return NULL; } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + // Set defaults np->desktop = desktop; np->object = object; @@ -289,16 +303,15 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, np->local_change = 0; np->show_handles = show_handles; np->helper_path = NULL; - np->helper_path_vec = new HelperPathList; - np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff); + np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff); np->helperpath_width = 1.0; np->curve = curve->copy(); - np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath", 0) == 1); + np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath"); if (SP_IS_LPE_ITEM(object)) { Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object)); if (lpe && lpe->isVisible() && lpe->showOrigPath()) { np->show_helperpath = true; - } + } } np->straight_path = false; if (IS_LIVEPATHEFFECT(object) && item) { @@ -307,6 +320,8 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, np->item = SP_ITEM(object); } + np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE); + // we need to update item's transform from the repr here, // because they may be out of sync when we respond // to a change in repr by regenerating nodepath --bb @@ -316,10 +331,15 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, np->d2i = np->i2d.inverse(); np->repr = repr; - if (repr_key_in) { // apparantly the object is an LPEObject + if (repr_key_in) { // apparently the object is an LPEObject np->repr_key = g_strdup(repr_key_in); np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL); - Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in); + Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe(); + if (!lpe) { + g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!"); + delete np; + } + Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in); if (lpeparam) { lpeparam->param_setup_nodepath(np); } @@ -361,7 +381,7 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, // Draw helper curve if (np->show_helperpath) { - np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true); + np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba); } sp_nodepath_create_helperpaths(np); @@ -372,48 +392,39 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, /** * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it. */ -void sp_nodepath_destroy(Inkscape::NodePath::Path *np) { - - if (!np) { //soft fail, like delete - return; - } - - while (np->subpaths) { - sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data); +Inkscape::NodePath::Path::~Path() { + while (this->subpaths) { + sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data); } //Inform the ShapeEditor that made me, if any, that I am gone. - if (np->shape_editor) - np->shape_editor->nodepath_destroyed(); + if (this->shape_editor) + this->shape_editor->nodepath_destroyed(); - g_assert(!np->selected); + g_assert(!this->selected); - if (np->helper_path) { - GtkObject *temp = np->helper_path; - np->helper_path = NULL; + if (this->helper_path) { + GtkObject *temp = this->helper_path; + this->helper_path = NULL; gtk_object_destroy(temp); } - if (np->curve) { - np->curve->unref(); - np->curve = NULL; + if (this->curve) { + this->curve->unref(); + this->curve = NULL; } - if (np->repr_key) { - g_free(np->repr_key); - np->repr_key = NULL; + if (this->repr_key) { + g_free(this->repr_key); + this->repr_key = NULL; } - if (np->repr_nodetypes_key) { - g_free(np->repr_nodetypes_key); - np->repr_nodetypes_key = NULL; + if (this->repr_nodetypes_key) { + g_free(this->repr_nodetypes_key); + this->repr_nodetypes_key = NULL; } - sp_nodepath_destroy_helperpaths(np); - delete np->helper_path_vec; - np->helper_path_vec = NULL; + sp_nodepath_destroy_helperpaths(this); - np->desktop = NULL; - - g_free(np); + this->desktop = NULL; } /** @@ -533,7 +544,7 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np); - NR::Point ppos = pit->initialPoint() * (Geom::Matrix)np->i2d; + Geom::Point ppos = pit->initialPoint() * np->i2d; NRPathcode pcode = NR_MOVETO; /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/ @@ -542,7 +553,7 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec dynamic_cast(&*cit) || dynamic_cast(&*cit) ) { - NR::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d; + Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d; sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos); ppos = cit->finalPoint() * (Geom::Matrix)np->i2d; @@ -550,8 +561,8 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec } else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast(&*cit)) { std::vector points = cubic_bezier->points(); - NR::Point pos = points[0] * (Geom::Matrix)np->i2d; - NR::Point npos = points[1] * (Geom::Matrix)np->i2d; + Geom::Point pos = points[0] * (Geom::Matrix)np->i2d; + Geom::Point npos = points[1] * (Geom::Matrix)np->i2d; sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos); ppos = points[2] * (Geom::Matrix)np->i2d; @@ -564,9 +575,9 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already * If the length is zero, don't add it to the nodepath. */ Geom::Curve const &closing_seg = pit->back_closed(); -// if ( ! closing_seg.isDegenerate() ) { + // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289) if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) { - NR::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d; + Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d; sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos); } @@ -593,6 +604,9 @@ Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length) case 's': typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH; break; + case 'a': + typestr[pos++] =Inkscape::NodePath::NODE_AUTO; + break; case 'z': typestr[pos++] =Inkscape::NodePath::NODE_SYMM; break; @@ -753,10 +767,13 @@ static SPCurve *create_curve(Inkscape::NodePath::Path *np) for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) { Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data; - curve->moveto(sp->first->pos * np->d2i); + curve->moveto(sp->first->pos * np->d2i); Inkscape::NodePath::Node *n = sp->first->n.other; while (n) { - NR::Point const end_pt = n->pos * np->d2i; + Geom::Point const end_pt = n->pos * np->d2i; + if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){ + g_message("niet finite"); + } switch (n->code) { case NR_LINETO: curve->lineto(end_pt); @@ -815,6 +832,9 @@ static gchar *create_typestr(Inkscape::NodePath::Path *np) case Inkscape::NodePath::NODE_SMOOTH: code = 's'; break; + case Inkscape::NodePath::NODE_AUTO: + code = 'a'; + break; case Inkscape::NodePath::NODE_SYMM: code = 'z'; break; @@ -890,16 +910,16 @@ static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscap new_path->code = NR_CURVETO; gdouble s = 1 - t; for (int dim = 0; dim < 2; dim++) { - NR::Coord const f000 = start->pos[dim]; - NR::Coord const f001 = start->n.pos[dim]; - NR::Coord const f011 = end->p.pos[dim]; - NR::Coord const f111 = end->pos[dim]; - NR::Coord const f00t = s * f000 + t * f001; - NR::Coord const f01t = s * f001 + t * f011; - NR::Coord const f11t = s * f011 + t * f111; - NR::Coord const f0tt = s * f00t + t * f01t; - NR::Coord const f1tt = s * f01t + t * f11t; - NR::Coord const fttt = s * f0tt + t * f1tt; + Geom::Coord const f000 = start->pos[dim]; + Geom::Coord const f001 = start->n.pos[dim]; + Geom::Coord const f011 = end->p.pos[dim]; + Geom::Coord const f111 = end->pos[dim]; + Geom::Coord const f00t = s * f000 + t * f001; + Geom::Coord const f01t = s * f001 + t * f011; + Geom::Coord const f11t = s * f011 + t * f111; + Geom::Coord const f0tt = s * f00t + t * f01t; + Geom::Coord const f1tt = s * f01t + t * f11t; + Geom::Coord const fttt = s * f0tt + t * f1tt; start->n.pos[dim] = f00t; new_path->p.pos[dim] = f0tt; new_path->pos[dim] = fttt; @@ -1044,8 +1064,20 @@ static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP); } } + + if (start->type == Inkscape::NodePath::NODE_AUTO) + start->type = Inkscape::NodePath::NODE_SMOOTH; + if (end->type == Inkscape::NodePath::NODE_AUTO) + end->type = Inkscape::NodePath::NODE_SMOOTH; + + start->n.pos = start->pos; + end->p.pos = end->pos; + + sp_node_adjust_handle(start, -1); + sp_node_adjust_handle(end, 1); + } else { - NR::Point delta = end->pos - start->pos; + Geom::Point delta = end->pos - start->pos; start->n.pos = start->pos + delta / 3; end->p.pos = end->pos - delta / 3; sp_node_adjust_handle(start, 1); @@ -1057,6 +1089,25 @@ static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode } } +static void +sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node) +{ + if (node->type == Inkscape::NodePath::NODE_CUSP) { + node->knot->setShape (SP_KNOT_SHAPE_DIAMOND); + node->knot->setSize (node->selected? 11 : 9); + sp_knot_update_ctrl(node->knot); + } else if (node->type == Inkscape::NodePath::NODE_AUTO) { + node->knot->setShape (SP_KNOT_SHAPE_CIRCLE); + node->knot->setSize (node->selected? 11 : 9); + sp_knot_update_ctrl(node->knot); + } else { + node->knot->setShape (SP_KNOT_SHAPE_SQUARE); + node->knot->setSize (node->selected? 9 : 7); + sp_knot_update_ctrl(node->knot); + } +} + + /** * Change node type, and its handles accordingly. */ @@ -1073,15 +1124,7 @@ static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::N node->type = type; - if (node->type == Inkscape::NodePath::NODE_CUSP) { - node->knot->setShape (SP_KNOT_SHAPE_DIAMOND); - node->knot->setSize (node->selected? 11 : 9); - sp_knot_update_ctrl(node->knot); - } else { - node->knot->setShape (SP_KNOT_SHAPE_SQUARE); - node->knot->setSize (node->selected? 9 : 7); - sp_knot_update_ctrl(node->knot); - } + sp_nodepath_update_node_knot(node); // if one of handles is mouseovered, preserve its position if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) { @@ -1114,28 +1157,35 @@ sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSi other_to_me = &othernode->n; } else if (&node->n == side) { other_to_me = &othernode->p; - } + } if (!other_to_me) return false; - bool is_line = - (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 && - NR::L2(node->pos - side->pos) < 1e-6); + bool is_line = + (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 && + Geom::L2(node->pos - side->pos) < 1e-6); return is_line; } /** * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from * lines to curves. If adjacent to one line segment, pulls out or rotates opposite handle to align - * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. + * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. * If already cusp and set to cusp, retracts handles. */ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type) { + if (type == Inkscape::NodePath::NODE_AUTO) { + if (node->p.other != NULL) + node->code = NR_CURVETO; + if (node->n.other != NULL) + node->n.other->code = NR_CURVETO; + } + if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) { -/* +/* Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode: - + if (two_handles) { // do nothing, adjust_handles called via set_node_type will line them up } else if (one_handle) { @@ -1156,8 +1206,8 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod } } */ - bool p_has_handle = (NR::L2(node->pos - node->p.pos) > 1e-6); - bool n_has_handle = (NR::L2(node->pos - node->n.pos) > 1e-6); + bool p_has_handle = (Geom::L2(node->pos - node->p.pos) > 1e-6); + bool n_has_handle = (Geom::L2(node->pos - node->n.pos) > 1e-6); bool p_is_line = sp_node_side_is_line(node, &node->p); bool n_is_line = sp_node_side_is_line(node, &node->n); @@ -1186,16 +1236,16 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod // pull n handle node->n.other->code = NR_CURVETO; double len = (type == Inkscape::NodePath::NODE_SYMM)? - NR::L2(node->p.pos - node->pos) : - NR::L2(node->n.other->pos - node->pos) / 3; - node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos); + Geom::L2(node->p.pos - node->pos) : + Geom::L2(node->n.other->pos - node->pos) / 3; + node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos); } else if (n_has_handle && node->p.other) { // pull p handle node->code = NR_CURVETO; double len = (type == Inkscape::NodePath::NODE_SYMM)? - NR::L2(node->n.pos - node->pos) : - NR::L2(node->p.other->pos - node->pos) / 3; - node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos); + Geom::L2(node->n.pos - node->pos) : + Geom::L2(node->p.other->pos - node->pos) / 3; + node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos); } } else if (!p_has_handle && !n_has_handle) { if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) { @@ -1206,43 +1256,22 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod node->code = NR_CURVETO; node->n.other->code = NR_CURVETO; - NR::Point leg_prev = node->pos - node->p.other->pos; - NR::Point leg_next = node->pos - node->n.other->pos; - - double norm_leg_prev = L2(leg_prev); - double norm_leg_next = L2(leg_next); - - NR::Point delta; - if (norm_leg_next > 0.0) { - delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev; - (&delta)->normalize(); - } - - if (type == Inkscape::NodePath::NODE_SYMM) { - double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2; - node->p.pos = node->pos + 0.3 * norm_leg_avg * delta; - node->n.pos = node->pos - 0.3 * norm_leg_avg * delta; - } else { - // length of handle is proportional to distance to adjacent node - node->p.pos = node->pos + 0.3 * norm_leg_prev * delta; - node->n.pos = node->pos - 0.3 * norm_leg_next * delta; - } - + sp_node_adjust_handles_auto(node); } else { // pull the handle opposite to line segment, making it half-smooth if (p_is_line && node->n.other) { if (type != Inkscape::NodePath::NODE_SYMM) { // pull n handle node->n.other->code = NR_CURVETO; - double len = NR::L2(node->n.other->pos - node->pos) / 3; - node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos); + double len = Geom::L2(node->n.other->pos - node->pos) / 3; + node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos); } } else if (n_is_line && node->p.other) { if (type != Inkscape::NodePath::NODE_SYMM) { // pull p handle node->code = NR_CURVETO; - double len = NR::L2(node->p.other->pos - node->pos) / 3; - node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos); + double len = Geom::L2(node->p.other->pos - node->pos) / 3; + node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos); } } } @@ -1259,13 +1288,18 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod /** * Move node to point, and adjust its and neighbouring handles. */ -void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) +void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p) { - NR::Point delta = p - node->pos; - node->pos = p; + if (node->type == Inkscape::NodePath::NODE_AUTO) { + node->pos = p; + sp_node_adjust_handles_auto(node); + } else { + Geom::Point delta = p - node->pos; + node->pos = p; - node->p.pos += delta; - node->n.pos += delta; + node->p.pos += delta; + node->n.pos += delta; + } Inkscape::NodePath::Node *node_p = NULL; Inkscape::NodePath::Node *node_n = NULL; @@ -1276,6 +1310,10 @@ void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) sp_node_adjust_handle(node->p.other, -1); node_p = node->p.other; } + if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) { + sp_node_adjust_handles_auto(node->p.other); + node_p = node->p.other; + } } if (node->n.other) { if (node->n.other->code == NR_LINETO) { @@ -1283,6 +1321,10 @@ void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) sp_node_adjust_handle(node->n.other, 1); node_n = node->n.other; } + if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) { + sp_node_adjust_handles_auto(node->n.other); + node_n = node->n.other; + } } // this function is only called from batch movers that will update display at the end @@ -1299,56 +1341,81 @@ void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) /** * Call sp_node_moveto() for node selection and handle possible snapping. */ -static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy, - bool const snap, bool constrained = false, +static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy, + bool const snap, bool constrained = false, Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point()) { - NR::Coord best = NR_HUGE; - NR::Point delta(dx, dy); - NR::Point best_pt = delta; - Inkscape::SnappedPoint best_abs; - - if (snap) { + Geom::Point delta(dx, dy); + Geom::Point best_pt = delta; + Inkscape::SnappedPoint best; + + if (snap) { /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and - * not to itself. The snapper however can not tell which nodes are selected and which are not, so we + * not to itself. The snapper however can not tell which nodes are selected and which are not, so we * must provide that information. */ - - // Build a list of the unselected nodes to which the snapper should snap - std::vector unselected_nodes; + + // Build a list of the unselected nodes to which the snapper should snap + std::vector > unselected_nodes; for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) { Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data; for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; if (!node->selected) { - unselected_nodes.push_back(to_2geom(node->pos)); - } + unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP)); + } } - } - + } + SnapManager &m = nodepath->desktop->namedview->snap_manager; - - for (GList *l = nodepath->selected; l != NULL; l = l->next) { + + // When only the node closest to the mouse pointer is to be snapped + // then we will not even try to snap to other points and discard those immediately + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool closest_only = prefs->getBool("/options/snapclosestonly/value", false); + + Inkscape::NodePath::Node *closest_node = NULL; + Geom::Coord closest_dist = NR_HUGE; + + if (closest_only) { + for (GList *l = nodepath->selected; l != NULL; l = l->next) { + Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; + Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin); + if (dist < closest_dist) { + closest_node = n; + closest_dist = dist; + } + } + } + + // Iterate through all selected nodes + m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes); + for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes); - Inkscape::SnappedPoint s; - if (constrained) { - Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; - dedicated_constraint.setPoint(n->pos); - s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint); - } else { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta)); - } - if (s.getSnapped() && (s.getDistance() < best)) { - best = s.getDistance(); - best_abs = s; - best_pt = from_2geom(s.getPoint()) - n->pos; + if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one + Inkscape::SnappedPoint s; + Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); + if (constrained) { + Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; + dedicated_constraint.setPoint(n->pos); + s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false); + } else { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type); + } + + if (s.getSnapped()) { + s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin)); + if (!s.isOtherSnapBetter(best, true)) { + best = s; + best_pt = from_2geom(s.getPoint()) - n->pos; + } + } } } - - if (best_abs.getSnapped()) { - nodepath->desktop->snapindicator->set_new_snappoint(best_abs); + + if (best.getSnapped()) { + nodepath->desktop->snapindicator->set_new_snaptarget(best); } else { - nodepath->desktop->snapindicator->remove_snappoint(); + nodepath->desktop->snapindicator->remove_snaptarget(); } } @@ -1395,16 +1462,16 @@ sculpt_profile (double x, double alpha, guint profile) } double -bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b) +bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b) { // extremely primitive for now, don't have time to look for the real one - double lower = NR::L2(b - a); - double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b); + double lower = Geom::L2(b - a); + double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b); return (lower + upper)/2; } void -sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p) +sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p) { n->pos = n->origin + delta; n->n.pos = n->n.origin + delta_n; @@ -1418,7 +1485,7 @@ sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, * on how far they are from the dragged node n. */ static void -sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta) +sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta) { g_assert (n); g_assert (nodepath); @@ -1434,7 +1501,8 @@ sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape:: if (pressure > 0.5) alpha = 1/alpha; - guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL); if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) { // Only one subpath has selected nodes: @@ -1505,8 +1573,8 @@ sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape:: if (n_node->selected) { sp_nodepath_move_node_and_handles (n_node, sculpt_profile (n_range / n_sel_range, alpha, profile) * delta, - sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta, - sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta); + sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta, + sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta); } if (n_node == p_node) { n_going = false; @@ -1522,8 +1590,8 @@ sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape:: if (p_node->selected) { sp_nodepath_move_node_and_handles (p_node, sculpt_profile (p_range / p_sel_range, alpha, profile) * delta, - sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta, - sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta); + sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta, + sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta); } if (p_node == n_node) { n_going = false; @@ -1535,7 +1603,7 @@ sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape:: } else { // Multiple subpaths have selected nodes: - // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2. + // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2. // TODO: correct these distances taking into account their angle relative to the bisector, so as to // fix the pear-like shape when sculpting e.g. a ring @@ -1546,7 +1614,7 @@ sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape:: for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data; if (node->selected) { - direct_range = MAX(direct_range, NR::L2(node->origin - n->origin)); + direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin)); } } } @@ -1559,9 +1627,9 @@ sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape:: if (node->selected) { if (direct_range > 1e-6) { sp_nodepath_move_node_and_handles (node, - sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta, - sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta, - sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta); + sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta, + sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta, + sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta); } else { sp_nodepath_move_node_and_handles (node, delta, delta, delta); } @@ -1637,7 +1705,7 @@ void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Co } /** - * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing + * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing */ boost::optional sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) { @@ -1647,7 +1715,7 @@ boost::optional sp_node_selected_common_coord (Inkscape::NodePath:: // determine coordinate of first selected node GList *nsel = nodepath->selected; Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data; - NR::Coord coord = n->pos[axis]; + Geom::Coord coord = n->pos[axis]; bool coincide = true; // compare it to the coordinates of all the other selected nodes @@ -1704,7 +1772,7 @@ static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gb Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which); NRPathcode code = sp_node_path_code_from_side(node, side); - show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6); + show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6); if (show_handle) { if (!side->knot) { // No handle knot at all @@ -1822,7 +1890,7 @@ void Inkscape::NodePath::Path::selection(std::list &l) /** * Align selected nodes on the specified axis. */ -void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis) +void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) { if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected return; @@ -1832,7 +1900,7 @@ void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axi return; } Inkscape::NodePath::Node *pNode = reinterpret_cast(nodepath->selected->data); - NR::Point dest(pNode->pos); + Geom::Point dest(pNode->pos); for (GList *l = nodepath->selected; l != NULL; l = l->next) { pNode = reinterpret_cast(l->data); if (pNode) { @@ -1848,9 +1916,9 @@ void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axi struct NodeSort { Inkscape::NodePath::Node *_node; - NR::Coord _coord; + Geom::Coord _coord; /// \todo use vectorof pointers instead of calling copy ctor - NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) : + NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) : _node(node), _coord(node->pos[axis]) {} @@ -1864,7 +1932,7 @@ static bool operator<(NodeSort const &a, NodeSort const &b) /** * Distribute selected nodes on the specified axis. */ -void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis) +void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) { if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected return; @@ -1897,7 +1965,7 @@ void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim it < sorted.end(); it ++ ) { - NR::Point dest((*it)._node->pos); + Geom::Point dest((*it)._node->pos); dest[axis] = pos; sp_node_moveto((*it)._node, dest); pos += step; @@ -1953,7 +2021,7 @@ sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath) * Select segment nearest to point */ void -sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle) +sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle) { if (!nodepath) { return; @@ -2001,7 +2069,7 @@ sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Po * Add a node nearest to point */ void -sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p) +sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p) { if (!nodepath) { return; @@ -2030,6 +2098,9 @@ sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p) //find segment to split Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index); + if (!e) { + return; + } //don't know why but t seems to flip for lines if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) { @@ -2055,7 +2126,7 @@ sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p) * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative() */ void -sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, NR::Point delta) +sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta) { Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node); @@ -2063,6 +2134,15 @@ sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, N g_return_if_fail(e != NULL); g_return_if_fail(&e->p != NULL); + if (e->type == Inkscape::NodePath::NODE_AUTO) { + e->type = Inkscape::NodePath::NODE_SMOOTH; + sp_nodepath_update_node_knot (e); + } + if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) { + e->p.other->type = Inkscape::NodePath::NODE_SMOOTH; + sp_nodepath_update_node_knot (e->p.other); + } + /* feel good is an arbitrary parameter that distributes the delta between handles * if t of the drag point is less than 1/6 distance form the endpoint only * the corresponding hadle is adjusted. This matches the behavior in GIMP @@ -2082,8 +2162,8 @@ sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, N sp_nodepath_set_line_type(e, NR_CURVETO); } - NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta; - NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta; + Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta; + Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta; e->p.other->n.pos += offsetcoord0; e->p.pos += offsetcoord1; @@ -2165,7 +2245,7 @@ static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape:: /* a and b are endpoints */ // if one of the two nodes is mouseovered, fix its position - NR::Point c; + Geom::Point c; if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) { c = a->pos; } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) { @@ -2188,7 +2268,7 @@ static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape:: /* a and b are separate subpaths */ Inkscape::NodePath::SubPath *sa = a->subpath; Inkscape::NodePath::SubPath *sb = b->subpath; - NR::Point p; + Geom::Point p; Inkscape::NodePath::Node *n; NRPathcode code; if (a == sa->first) { @@ -2222,13 +2302,13 @@ static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape:: } if (b == sb->first) { - // copy all nodes from b to a, forward + // copy all nodes from b to a, forward sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos); for (n = sb->first->n.other; n != NULL; n = n->n.other) { sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos); } } else if (b == sb->last) { - // copy all nodes from b to a, backward + // copy all nodes from b to a, backward sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos); for (n = sb->last->p.other; n != NULL; n = n->p.other) { sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos); @@ -2279,7 +2359,7 @@ static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, In Inkscape::NodePath::SubPath *sb = b->subpath; Inkscape::NodePath::Node *n; - NR::Point p; + Geom::Point p; NRPathcode code; if (a == sa->first) { code = (NRPathcode) sa->first->n.other->code; @@ -2415,7 +2495,7 @@ void sp_node_delete_preserve(GList *nodes_to_delete) //calculate points for each segment int rate = 5; float period = 1.0 / rate; - std::vector data; + std::vector data; if (!just_delete) { data.push_back(sample_cursor->pos); for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) { @@ -2426,15 +2506,15 @@ void sp_node_delete_preserve(GList *nodes_to_delete) } //sample points on the contiguous selected segment - NR::Point *bez; - bez = new NR::Point [4]; + Geom::Point *bez; + bez = new Geom::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); @@ -2449,16 +2529,16 @@ void sp_node_delete_preserve(GList *nodes_to_delete) if (!just_delete) { //calculate the best fitting single segment and adjust the endpoints - NR::Point *adata; - adata = new NR::Point [data.size()]; + Geom::Point *adata; + adata = new Geom::Point [data.size()]; copy(data.begin(), data.end(), adata); - NR::Point *bez; - bez = new NR::Point [4]; + Geom::Point *bez; + bez = new Geom::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); + ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error); //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync. //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved, @@ -2758,12 +2838,12 @@ static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean select node->selected = selected; if (selected) { - node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9); + node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9); node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI); node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI); sp_knot_update_ctrl(node->knot); } else { - node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7); + node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7); node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI); node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI); sp_knot_update_ctrl(node->knot); @@ -2997,7 +3077,7 @@ void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath) /** * \brief Select all nodes that are within the rectangle. */ -void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental) +void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental) { if (!incremental) { sp_nodepath_deselect(nodepath); @@ -3131,13 +3211,13 @@ nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape: if (node == n) continue; if (node->selected) { - if (NR::L2(node->pos - n->pos) > farthest_dist) { - farthest_dist = NR::L2(node->pos - n->pos); + if (Geom::L2(node->pos - n->pos) > farthest_dist) { + farthest_dist = Geom::L2(node->pos - n->pos); farthest_selected = node; } } else { - if (NR::L2(node->pos - n->pos) < closest_dist) { - closest_dist = NR::L2(node->pos - n->pos); + if (Geom::L2(node->pos - n->pos) < closest_dist) { + closest_dist = Geom::L2(node->pos - n->pos); closest_unselected = node; } } @@ -3223,6 +3303,10 @@ static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adj { g_assert(node); + // nothing to do for auto nodes (sp_node_adjust_handles() does the job) + if (node->type == Inkscape::NodePath::NODE_AUTO) + return; + Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust); Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me); @@ -3245,9 +3329,9 @@ static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adj if (sp_node_side_is_line(node, other)) { // other is a line, and we are either smooth or symm Inkscape::NodePath::Node *othernode = other->other; - double len = NR::L2(me->pos - node->pos); - NR::Point delta = node->pos - othernode->pos; - double linelen = NR::L2(delta); + double len = Geom::L2(me->pos - node->pos); + Geom::Point delta = node->pos - othernode->pos; + double linelen = Geom::L2(delta); if (linelen < 1e-18) return; me->pos = node->pos + (len / linelen)*delta; @@ -3255,14 +3339,14 @@ static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adj } if (node->type == Inkscape::NodePath::NODE_SYMM) { - // symmetrize + // symmetrize me->pos = 2 * node->pos - other->pos; return; } else { // smoothify - double len = NR::L2(me->pos - node->pos); - NR::Point delta = other->pos - node->pos; - double otherlen = NR::L2(delta); + double len = Geom::L2(me->pos - node->pos); + Geom::Point delta = other->pos - node->pos; + double otherlen = Geom::L2(delta); if (otherlen < 1e-18) return; me->pos = node->pos - (len / otherlen) * delta; } @@ -3282,6 +3366,11 @@ static void sp_node_adjust_handles(Inkscape::NodePath::Node *node) if (node->p.other == NULL) return; if (node->n.other == NULL) return; + if (node->type == Inkscape::NodePath::NODE_AUTO) { + sp_node_adjust_handles_auto(node); + return; + } + if (sp_node_side_is_line(node, &node->p)) { sp_node_adjust_handle(node, 1); return; @@ -3293,7 +3382,7 @@ static void sp_node_adjust_handles(Inkscape::NodePath::Node *node) } /* both are curves */ - NR::Point const delta( node->n.pos - node->p.pos ); + Geom::Point const delta( node->n.pos - node->p.pos ); if (node->type == Inkscape::NodePath::NODE_SYMM) { node->p.pos = node->pos - delta / 2; @@ -3302,14 +3391,38 @@ static void sp_node_adjust_handles(Inkscape::NodePath::Node *node) } /* We are smooth */ - double plen = NR::L2(node->p.pos - node->pos); + double plen = Geom::L2(node->p.pos - node->pos); if (plen < 1e-18) return; - double nlen = NR::L2(node->n.pos - node->pos); + double nlen = Geom::L2(node->n.pos - node->pos); if (nlen < 1e-18) return; node->p.pos = node->pos - (plen / (plen + nlen)) * delta; node->n.pos = node->pos + (nlen / (plen + nlen)) * delta; } +static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node) +{ + if (node->p.other == NULL || node->n.other == NULL) { + node->p.pos = node->pos; + node->n.pos = node->pos; + return; + } + + Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos); + Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos); + + double norm_leg_prev = Geom::L2(leg_prev); + double norm_leg_next = Geom::L2(leg_next); + + Geom::Point delta; + if (norm_leg_next > 0.0) { + delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev; + delta.normalize(); + } + + node->p.pos = node->pos - norm_leg_prev / 3 * delta; + node->n.pos = node->pos + norm_leg_next / 3 * delta; +} + /** * Node event callback. */ @@ -3413,6 +3526,10 @@ gboolean node_key(GdkEvent *event) sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH); ret = TRUE; break; + case GDK_a: + sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO); + ret = TRUE; + break; case GDK_y: sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM); ret = TRUE; @@ -3442,6 +3559,8 @@ static void node_clicked(SPKnot */*knot*/, guint state, gpointer data) sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH); } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) { sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM); + } else if (n->type == Inkscape::NodePath::NODE_SYMM) { + sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO); } else { sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP); } @@ -3462,7 +3581,7 @@ static void node_clicked(SPKnot */*knot*/, guint state, gpointer data) /** * Mouse grabbed node callback. */ -static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data) +static void node_grabbed(SPKnot *knot, guint state, gpointer data) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; @@ -3471,6 +3590,9 @@ static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data) } n->is_dragging = true; + // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping) + n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin; + sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5); sp_nodepath_remember_origins (n->subpath->nodepath); @@ -3485,6 +3607,7 @@ static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data) n->dragging_out = NULL; n->is_dragging = false; + n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE); sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas); sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes")); @@ -3497,13 +3620,13 @@ static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data) * \param closest Pointer to the point struct where the result is stored. * \todo FIXME: use dot product perhaps? */ -static void point_line_closest(NR::Point *p, double a, NR::Point *closest) +static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest) { if (a == HUGE_VAL) { // vertical - *closest = NR::Point(0, (*p)[NR::Y]); + *closest = Geom::Point(0, (*p)[Geom::Y]); } else { - (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1); - (*closest)[NR::Y] = a * (*closest)[NR::X]; + (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1); + (*closest)[Geom::Y] = a * (*closest)[Geom::X]; } } @@ -3512,11 +3635,11 @@ static void point_line_closest(NR::Point *p, double a, NR::Point *closest) * \param p A point. * \param a Angle of the line; it is assumed to go through coordinate origin. */ -static double point_line_distance(NR::Point *p, double a) +static double point_line_distance(Geom::Point *p, double a) { - NR::Point c; + Geom::Point c; point_line_closest(p, a, &c); - return sqrt(((*p)[NR::X] - c[NR::X])*((*p)[NR::X] - c[NR::X]) + ((*p)[NR::Y] - c[NR::Y])*((*p)[NR::Y] - c[NR::Y])); + return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y])); } /** @@ -3524,30 +3647,30 @@ static double point_line_distance(NR::Point *p, double a) * \todo fixme: This goes to "moved" event? (lauris) */ static gboolean -node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) +node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data) { double yn, xn, yp, xp; double an, ap, na, pa; double d_an, d_ap, d_na, d_pa; gboolean collinear = FALSE; - NR::Point c; - NR::Point pr; + Geom::Point c; + Geom::Point pr; Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; - n->subpath->nodepath->desktop->snapindicator->remove_snappoint(); + n->subpath->nodepath->desktop->snapindicator->remove_snaptarget(); // If either (Shift and some handle retracted), or (we're already dragging out a handle) if ( (!n->subpath->nodepath->straight_path) && ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out ) ) { - NR::Point mouse = (*p); + Geom::Point mouse = p; if (!n->dragging_out) { // This is the first drag-out event; find out which handle to drag out - double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL); - double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL); + double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL); + double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL); if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node? return FALSE; @@ -3571,8 +3694,8 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) opposite = &n->p; n->n.other->code = NR_CURVETO; } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other - double appr_other_n = (n->n.other ? NR::L2(n->n.other->n.pos - n->pos) - NR::L2(n->n.other->n.pos - (*p)) : -HUGE_VAL); - double appr_other_p = (n->n.other ? NR::L2(n->n.other->p.pos - n->pos) - NR::L2(n->n.other->p.pos - (*p)) : -HUGE_VAL); + double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - p) : -HUGE_VAL); + double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - p) : -HUGE_VAL); if (appr_other_p > appr_other_n) { // closer to other's p handle n->dragging_out = &n->n; opposite = &n->p; @@ -3587,7 +3710,7 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) // if there's another handle, make sure the one we drag out starts parallel to it if (opposite->pos != n->pos) { - mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos); + mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos); } // knots might not be created yet! @@ -3596,7 +3719,7 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) } // pass this on to the handle-moved callback - node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n); + node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n); sp_node_update_handles(n); return TRUE; } @@ -3605,28 +3728,28 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) // calculate relative distances of handles // n handle: - yn = n->n.pos[NR::Y] - n->pos[NR::Y]; - xn = n->n.pos[NR::X] - n->pos[NR::X]; + yn = n->n.pos[Geom::Y] - n->pos[Geom::Y]; + xn = n->n.pos[Geom::X] - n->pos[Geom::X]; // if there's no n handle (straight line), see if we can use the direction to the next point on path if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) { if (n->n.other) { // if there is the next point if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either - yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag - xn = n->n.other->origin[NR::X] - n->origin[NR::X]; + yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag + xn = n->n.other->origin[Geom::X] - n->origin[Geom::X]; } } if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi if (yn < 0) { xn = -xn; yn = -yn; } // p handle: - yp = n->p.pos[NR::Y] - n->pos[NR::Y]; - xp = n->p.pos[NR::X] - n->pos[NR::X]; + yp = n->p.pos[Geom::Y] - n->pos[Geom::Y]; + xp = n->p.pos[Geom::X] - n->pos[Geom::X]; // if there's no p handle (straight line), see if we can use the direction to the prev point on path if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) { if (n->p.other) { if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6) - yp = n->p.other->origin[NR::Y] - n->origin[NR::Y]; - xp = n->p.other->origin[NR::X] - n->origin[NR::X]; + yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y]; + xp = n->p.other->origin[Geom::X] - n->origin[Geom::X]; } } if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi @@ -3659,7 +3782,7 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) if (ap == 0) pa = HUGE_VAL; else pa = -1/ap; // mouse point relative to the node's original pos - pr = (*p) - n->origin; + pr = p - n->origin; // distances to the four lines (two handles and two perpendiculars) d_an = point_line_distance(&pr, an); @@ -3680,40 +3803,40 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) // move the node to the closest point sp_nodepath_selected_nodes_move(n->subpath->nodepath, - n->origin[NR::X] + c[NR::X] - n->pos[NR::X], - n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], + n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X], + n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], true); } else { // constraining to hor/vert - if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor + if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor sp_nodepath_selected_nodes_move(n->subpath->nodepath, - (*p)[NR::X] - n->pos[NR::X], - n->origin[NR::Y] - n->pos[NR::Y], - true, - true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X])); + p[Geom::X] - n->pos[Geom::X], + n->origin[Geom::Y] - n->pos[Geom::Y], + true, + true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X])); } else { // snap to vert sp_nodepath_selected_nodes_move(n->subpath->nodepath, - n->origin[NR::X] - n->pos[NR::X], - (*p)[NR::Y] - n->pos[NR::Y], + n->origin[Geom::X] - n->pos[Geom::X], + p[Geom::Y] - n->pos[Geom::Y], true, - true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y])); + true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y])); } } } else { // move freely if (n->is_dragging) { if (state & GDK_MOD1_MASK) { // sculpt - sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin); + sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin); } else { sp_nodepath_selected_nodes_move(n->subpath->nodepath, - (*p)[NR::X] - n->pos[NR::X], - (*p)[NR::Y] - n->pos[NR::Y], + p[Geom::X] - n->pos[Geom::X], + p[Geom::Y] - n->pos[Geom::Y], (state & GDK_SHIFT_MASK) == 0); } } } - n->subpath->nodepath->desktop->scroll_to_point(*p); + n->subpath->nodepath->desktop->scroll_to_point(p); return TRUE; } @@ -3748,6 +3871,12 @@ static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; + // convert auto -> smooth when dragging handle + if (n->type == Inkscape::NodePath::NODE_AUTO) { + n->type = Inkscape::NodePath::NODE_SMOOTH; + sp_nodepath_update_node_knot (n); + } + if (!n->selected) { sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE); } @@ -3788,7 +3917,7 @@ static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data) /** * Node handle "request" signal callback. */ -static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data) +static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; @@ -3812,44 +3941,43 @@ static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpo SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop, true, n->subpath->nodepath->item); Inkscape::SnappedPoint s; - + if ((state & GDK_SHIFT_MASK) != 0) { // We will not try to snap when the shift-key is pressed - // so remove the old snap indicator and don't wait for it to time-out - desktop->snapindicator->remove_snappoint(); + // so remove the old snap indicator and don't wait for it to time-out + desktop->snapindicator->remove_snaptarget(); } Inkscape::NodePath::Node *othernode = opposite->other; + Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP); if (othernode) { if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) { /* We are smooth node adjacent with line */ - NR::Point const delta = *p - n->pos; - NR::Coord const len = NR::L2(delta); + Geom::Point const delta = p - n->pos; + Geom::Coord const len = Geom::L2(delta); Inkscape::NodePath::Node *othernode = opposite->other; - NR::Point const ndelta = n->pos - othernode->pos; - NR::Coord const linelen = NR::L2(ndelta); + Geom::Point const ndelta = n->pos - othernode->pos; + Geom::Coord const linelen = Geom::L2(ndelta); if (len > NR_EPSILON && linelen > NR_EPSILON) { - NR::Coord const scal = dot(delta, ndelta) / linelen; - (*p) = n->pos + (scal / linelen) * ndelta; + Geom::Coord const scal = dot(delta, ndelta) / linelen; + p = n->pos + (scal / linelen) * ndelta; } if ((state & GDK_SHIFT_MASK) == 0) { - s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta)); + s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false); } } else { - if ((state & GDK_SHIFT_MASK) == 0) { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p)); - } + if ((state & GDK_SHIFT_MASK) == 0) { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type); + } } } else { - if ((state & GDK_SHIFT_MASK) == 0) { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p)); - } - } - - Geom::Point pt2g = *p; - s.getPoint(pt2g); - *p = pt2g; - + if ((state & GDK_SHIFT_MASK) == 0) { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type); + } + } + + s.getPoint(p); + sp_node_adjust_handle(n, -which); return FALSE; @@ -3858,9 +3986,10 @@ static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpo /** * Node handle moved callback. */ -static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data) +static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::NodePath::NodeSide *me; Inkscape::NodePath::NodeSide *other; @@ -3879,10 +4008,10 @@ static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point Radial rme(me->pos - n->pos); Radial rother(other->pos - n->pos); - Radial rnew(*p - n->pos); + Radial rnew(p - n->pos); if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) { - int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12); + int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12); /* 0 interpreted as "no snapping". */ // 1. Snap to the closest PI/snaps angle, starting from zero. @@ -3902,13 +4031,13 @@ static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer // 3. Snap to the angle of the opposite line, if any Inkscape::NodePath::Node *othernode = other->other; if (othernode) { - NR::Point other_to_snap(0,0); + Geom::Point other_to_snap(0,0); if (sp_node_side_is_line(n, other)) { other_to_snap = othernode->pos - n->pos; } else { other_to_snap = other->pos - n->pos; } - if (NR::L2(other_to_snap) > 1e-3) { + if (Geom::L2(other_to_snap) > 1e-3) { Radial rother_to_snap(other_to_snap); /* The closest PI/2 angle, starting from the angle of the opposite line segment */ double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2); @@ -3928,18 +4057,18 @@ static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer rnew.r = me->origin_radial.r; } - if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK)) - && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) { + if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK))) + && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) { // rotate the other handle correspondingly, if both old and new angles exist and are not the same rother.a += rnew.a - rme.a; - other->pos = NR::Point(rother) + n->pos; + other->pos = Geom::Point(rother) + n->pos; if (other->knot) { sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos); sp_knot_moveto(other->knot, other->pos); } } - me->pos = NR::Point(rnew) + n->pos; + me->pos = Geom::Point(rnew) + n->pos; sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos); // move knot, but without emitting the signal: @@ -3961,7 +4090,7 @@ static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer double degrees = 180 / M_PI * rnew.a; if (degrees > 180) degrees -= 360; if (degrees < -180) degrees += 360; - if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0) + if (prefs->getBool("/options/compassangledisplay/value")) degrees = angle_to_compass (degrees); GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric()); @@ -3994,12 +4123,12 @@ static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePa break; case GDK_ENTER_NOTIFY: // we use an experimentally determined threshold that seems to work fine - if (NR::L2(n->pos - knot->pos) < 0.75) + if (Geom::L2(n->pos - knot->pos) < 0.75) Inkscape::NodePath::Path::active_node = n; break; case GDK_LEAVE_NOTIFY: // we use an experimentally determined threshold that seems to work fine - if (NR::L2(n->pos - knot->pos) < 0.75) + if (Geom::L2(n->pos - knot->pos) < 0.75) Inkscape::NodePath::Path::active_node = NULL; break; default: @@ -4058,8 +4187,8 @@ static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int whi Inkscape::NodePath::NodeSide *me, *other; bool both = false; - double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X]; - double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X]; + double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X]; + double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X]; if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which" me = &(n->p); @@ -4100,10 +4229,10 @@ static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int whi node_rotate_one_internal (*n, angle, rme, rother, both); } - me->pos = n->pos + NR::Point(rme); + me->pos = n->pos + Geom::Point(rme); if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) { - other->pos = n->pos + NR::Point(rother); + other->pos = n->pos + Geom::Point(rother); } // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end, @@ -4125,7 +4254,7 @@ void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdoub // rotate as an object: Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; - NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node + Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; box.expandTo (n->pos); // contain all selected nodes @@ -4135,22 +4264,22 @@ void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdoub if (screen) { gdouble const zoom = nodepath->desktop->current_zoom(); gdouble const zmove = angle / zoom; - gdouble const r = NR::L2(box.max() - box.midpoint()); + gdouble const r = Geom::L2(box.max() - box.midpoint()); rot = atan2(zmove, r); } else { rot = angle; } - NR::Point rot_center; + Geom::Point rot_center; if (Inkscape::NodePath::Path::active_node == NULL) rot_center = box.midpoint(); else rot_center = Inkscape::NodePath::Path::active_node->pos; - NR::Matrix t = - NR::Matrix (NR::translate(-rot_center)) * - NR::Matrix (NR::rotate(rot)) * - NR::Matrix (NR::translate(rot_center)); + Geom::Matrix t = + Geom::Matrix (Geom::Translate(-rot_center)) * + Geom::Matrix (Geom::Rotate(rot)) * + Geom::Matrix (Geom::Translate(rot_center)); for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; @@ -4172,8 +4301,8 @@ static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which bool both = false; Inkscape::NodePath::NodeSide *me, *other; - double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X]; - double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X]; + double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X]; + double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X]; if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which" me = &(n->p); @@ -4238,10 +4367,10 @@ static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which } } - me->pos = n->pos + NR::Point(rme); + me->pos = n->pos + Geom::Point(rme); if (both || n->type == Inkscape::NodePath::NODE_SYMM) { - other->pos = n->pos + NR::Point(rother); + other->pos = n->pos + Geom::Point(rother); } // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end, @@ -4264,24 +4393,34 @@ void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdoubl // scale nodes as an "object": Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data; - NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node + Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; box.expandTo (n->pos); // contain all selected nodes } + if ( Geom::are_near(box.maxExtent(), 0) ) { + SPEventContext *ec = nodepath->desktop->event_context; + if (!ec) return; + Inkscape::MessageContext *mc = get_message_context(ec); + if (!mc) return; + mc->setF(Inkscape::WARNING_MESSAGE, + _("Cannot scale nodes when all are at the same location.")); + return; + } double scale = (box.maxExtent() + grow)/box.maxExtent(); - NR::Point scale_center; + + Geom::Point scale_center; if (Inkscape::NodePath::Path::active_node == NULL) scale_center = box.midpoint(); else scale_center = Inkscape::NodePath::Path::active_node->pos; - NR::Matrix t = - NR::Matrix (NR::translate(-scale_center)) * - NR::Matrix (NR::scale(scale, scale)) * - NR::Matrix (NR::translate(scale_center)); + Geom::Matrix t = + Geom::Translate(-scale_center) * + Geom::Scale(scale, scale) * + Geom::Translate(scale_center); for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; @@ -4304,7 +4443,7 @@ void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, /** * Flip selected nodes horizontally/vertically. */ -void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional center) +void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional center) { if (!nodepath || !nodepath->selected) return; @@ -4322,10 +4461,10 @@ void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost: if (!center) { center = box.midpoint(); } - NR::Matrix t = - NR::Matrix (NR::translate(- *center)) * - NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) * - NR::Matrix (NR::translate(*center)); + Geom::Matrix t = + Geom::Matrix (Geom::Translate(- *center)) * + Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) * + Geom::Matrix (Geom::Translate(*center)); for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; @@ -4446,7 +4585,7 @@ static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::N * \param npos Handle position in previous direction */ Inkscape::NodePath::Node * -sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, NR::Point *ppos, NR::Point *pos, NR::Point *npos) +sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos) { g_assert(sp); g_assert(sp->nodepath); @@ -4465,7 +4604,7 @@ sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node * } else { if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) { // points are (almost) collinear - if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) { + if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) { // endnode, or a node with a retracted handle n->type = Inkscape::NodePath::NODE_CUSP; } else { @@ -4508,12 +4647,11 @@ sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node * n->knot = sp_knot_new(sp->nodepath->desktop, _("Node: drag to edit the path; with Ctrl to snap to horizontal/vertical; with Ctrl+Alt to snap to handles' directions")); sp_knot_set_position(n->knot, *pos, 0); - n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE); - n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7); n->knot->setAnchor (GTK_ANCHOR_CENTER); n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI); n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI); - sp_knot_update_ctrl(n->knot); + + sp_nodepath_update_node_knot(n); g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n); g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n); @@ -4730,7 +4868,7 @@ static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node) for (int which = -1; which <= 1; which += 2) { Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which); - if (side->other && NR::L2(side->pos - node->pos) < 1e-6) + if (side->other && Geom::L2(side->pos - node->pos) < 1e-6) retracted ++; if (!side->other) endnode = true; @@ -4748,6 +4886,8 @@ static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node) case Inkscape::NodePath::NODE_SMOOTH: // TRANSLATORS: "smooth" is an adjective here return _("smooth"); + case Inkscape::NodePath::NODE_AUTO: + return _("auto"); case Inkscape::NodePath::NODE_SYMM: return _("symmetric"); } @@ -4874,26 +5014,29 @@ void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) { sp_shape_set_curve(SP_SHAPE(np->object), curve, true); } } else if ( IS_LIVEPATHEFFECT(np->object) ) { - Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) ); - if (pathparam) { - pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG - np->object->requestModified(SP_OBJECT_MODIFIED_FLAG); + Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe(); + if (lpe) { + Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast( lpe->getParameter(np->repr_key) ); + if (pathparam) { + pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG + np->object->requestModified(SP_OBJECT_MODIFIED_FLAG); + } } } } -/** +/* SPCanvasItem * sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) { return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path)); } -**/ +*/ -/** + +/// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file SPCanvasItem * -sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) { +sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) { SPCurve *flash_curve = curve->copy(); - Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity(); flash_curve->transform(i2d); SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve); // would be nice if its color could be XORed or something, now it is invisible for red stroked objects... @@ -4906,28 +5049,38 @@ sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem } SPCanvasItem * -sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) { - return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path), - prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff)); -} -**/ +sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) { + if (!item || !desktop) { + return NULL; + } -SPCanvasItem * -sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) { - SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy(); - Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path)); - flash_curve->transform(i2d); - SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve); - // would be nice if its color could be XORed or something, now it is invisible for red stroked objects... - // unless we also flash the nodes... - guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO); - sp_canvas_item_show(canvasitem); - flash_curve->unref(); - return canvasitem; + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff); + + Geom::Matrix i2d = sp_item_i2d_affine(item); + + SPCurve *curve = NULL; + if (SP_IS_PATH(item)) { + curve = sp_path_get_curve_for_edit(SP_PATH(item)); + } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) { + curve = sp_shape_get_curve (SP_SHAPE(item)); + } else if ( SP_IS_TEXT(item) ) { + // do not display helperpath for text - we cannot do anything with it in Node tool anyway + // curve = SP_TEXT(item)->getNormalizedBpath(); + return NULL; + } else { + g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n"); + return NULL; + } + + SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color); + + curve->unref(); + + return helperpath; } + // TODO: Merge this with sp_nodepath_make_helper_item()! void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) { np->show_helperpath = show;