X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fnodepath.cpp;h=8f17ae0133122477bd60f9583553eed265fa15fb;hb=eb4f7092b0b8482c042370d8c6520d4b05997afe;hp=d50ec769e311ced918c45c8730042169eac6b890;hpb=58b91d896aa27cde4d36dfd8eae0a2652e04b4d1;p=inkscape.git diff --git a/src/nodepath.cpp b/src/nodepath.cpp index d50ec769e..8f17ae013 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -21,9 +21,12 @@ #include "display/sodipodi-ctrl.h" #include "display/sp-canvas-util.h" #include -#include "libnr/n-art-bpath.h" -#include "libnr/nr-path.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" #include "inkscape.h" #include "document.h" @@ -34,29 +37,34 @@ #include "message-stack.h" #include "message-context.h" #include "node-context.h" +#include "lpe-tool-context.h" #include "shape-editor.h" #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 "splivarot.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" #include "live_effects/parameter/parameter.h" +#include "live_effects/parameter/path.h" #include "util/mathfns.h" #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! @@ -94,8 +102,8 @@ static GMemChunk *nodechunk = NULL; /* Creation from object */ -static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t); -static gchar *parse_nodetypes(gchar const *types, gint length); +static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t); +static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length); /* Object updating */ @@ -114,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 */ @@ -136,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 */ @@ -151,11 +160,103 @@ static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) // active_node indicates mouseover node 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, 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), 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) { + sp_canvas_item_show(helper_path); + } + helper_curve->unref(); + return helper_path; +} + +static void +sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) { + //std::map > helper_path_vec; + if (!SP_IS_LPE_ITEM(np->item)) { + g_print ("Only LPEItems can have helperpaths!\n"); + return; + } + + 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::LPEObjectReference *lperef = (*i); + 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(); + } + } + } +} + +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 (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(); +} + +/** updates canvas items from the effect's helper paths */ +void +sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) { + //std::map > helper_path_vec; + if (!SP_IS_LPE_ITEM(np->item)) { + g_print ("Only LPEItems can have helperpaths!\n"); + return; + } + + SPLPEItem *lpeitem = SP_LPE_ITEM(np->item); + PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem); + + /* The number or type or LPEs may have changed, so we need to clear and recreate our + * helper_path_vec to make sure it is in sync */ + sp_nodepath_destroy_helperpaths(np); + sp_nodepath_create_helperpaths(np); + + for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) { + Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe(); + if (lpe) { + 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(); + } + } + } +} + /** * \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 @@ -177,37 +278,43 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in); - if (curve == NULL) + if (curve == NULL) { return NULL; + } - NArtBpath *bpath = curve->first_bpath(); - gint length = curve->end; - if (length == 0) { + if (curve->get_segment_count() < 1) { curve->unref(); return NULL; // prevent crash for one-node paths } //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; np->subpaths = NULL; np->selected = NULL; np->shape_editor = NULL; //Let the shapeeditor that makes this set it - np->livarot_path = NULL; np->local_change = 0; np->show_handles = show_handles; np->helper_path = NULL; - 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) { np->item = item; @@ -215,6 +322,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 @@ -224,10 +333,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); } @@ -236,119 +350,96 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) { np->repr_key = g_strdup("inkscape:original-d"); - LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object)); - if (lpeobj && lpeobj->lpe) { - lpeobj->lpe->setup_nodepath(np); + Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object)); + if (lpe) { + lpe->setup_nodepath(np); } } else { np->repr_key = g_strdup("d"); } } + /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice. + * So for example a closed rectangle has a nodetypestring of length 5. + * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */ + Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector()); + np->curve->set_pathvector(pathv_sanitized); + guint length = np->curve->get_segment_count(); + for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) { + length += pit->empty() ? 0 : 1; + } + gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key); - gchar *typestr = parse_nodetypes(nodetypes, length); + Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length); // create the subpath(s) from the bpath - NArtBpath *b = bpath; - while (b->code != NR_END) { - b = subpath_from_bpath(np, b, typestr + (b - bpath)); - } + subpaths_from_pathvector(np, pathv_sanitized, typestr); // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed) np->subpaths = g_list_reverse(np->subpaths); - g_free(typestr); + delete[] typestr; curve->unref(); - // create the livarot representation from the same item - sp_nodepath_ensure_livarot_path(np); - // Draw helper curve if (np->show_helperpath) { - SPCurve *helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d ); - np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve); - sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); - sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO); - sp_canvas_item_move_to_z(np->helper_path, 0); - sp_canvas_item_show(np->helper_path); - helper_curve->unref(); + np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba); } + sp_nodepath_create_helperpaths(np); + return np; } /** * 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); - - if (np->livarot_path) { - delete np->livarot_path; - np->livarot_path = NULL; - } + 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; } - np->desktop = NULL; + sp_nodepath_destroy_helperpaths(this); - g_free(np); + this->desktop = NULL; } - -void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np) -{ - if (np && np->livarot_path == NULL) { - SPCurve *curve = create_curve(np); - NArtBpath *bpath = SP_CURVE_BPATH(curve); - np->livarot_path = bpath_to_Path(bpath); - - if (np->livarot_path) - np->livarot_path->ConvertWithBackData(0.01); - - curve->unref(); - } -} - - /** * Return the node count of a given NodeSubPath. */ static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath) { - if (!subpath) - return 0; - gint nodeCount = g_list_length(subpath->nodes); + int nodeCount = 0; + + if (subpath) { + nodeCount = g_list_length(subpath->nodes); + } + return nodeCount; } @@ -357,12 +448,12 @@ static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subp */ static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np) { - if (!np) - return 0; gint nodeCount = 0; - for (GList *item = np->subpaths ; item ; item=item->next) { - Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data; - nodeCount += g_list_length(subpath->nodes); + if (np) { + for (GList *item = np->subpaths ; item ; item=item->next) { + Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data; + nodeCount += g_list_length(subpath->nodes); + } } return nodeCount; } @@ -372,9 +463,11 @@ static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np) */ static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np) { - if (!np) - return 0; - return g_list_length (np->subpaths); + gint nodeCount = 0; + if (np) { + nodeCount = g_list_length(np->subpaths); + } + return nodeCount; } /** @@ -382,9 +475,11 @@ static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np) */ static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np) { - if (!np) - return 0; - return g_list_length (np->selected); + gint nodeCount = 0; + if (np) { + nodeCount = g_list_length(np->selected); + } + return nodeCount; } /** @@ -392,24 +487,24 @@ static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np) */ static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np) { - if (!np) - return 0; - if (!np->selected) - return 0; - if (!np->selected->next) - return 1; - gint count = 0; - for (GList *spl = np->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) { - count ++; - break; + gint nodeCount = 0; + if (np && np->selected) { + if (!np->selected->next) { + nodeCount = 1; + } else { + for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) { + Inkscape::NodePath::SubPath *subpath = static_cast(spl->data); + for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) { + Inkscape::NodePath::Node *node = static_cast(nl->data); + if (node->selected) { + nodeCount++; + break; + } + } } } } - return count; + return nodeCount; } /** @@ -439,73 +534,81 @@ static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath) } /** - * Create new nodepath from b, make it subpath of np. - * \param t The node type. - * \todo Fixme: t should be a proper type, rather than gchar + * Create new nodepaths from pathvector, make it subpaths of np. + * \param t The node type array. */ -static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t) +static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t) { - NR::Point ppos, pos, npos; - - g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN)); + guint i = 0; // index into node type array + for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { + if (pit->empty()) + continue; // don't add single knot paths + + Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np); + + 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)*/ + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) { + if( dynamic_cast(&*cit) || + dynamic_cast(&*cit) || + dynamic_cast(&*cit) ) + { + 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; + pcode = NR_LINETO; + } + else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast(&*cit)) { + std::vector points = cubic_bezier->points(); + 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; + pcode = NR_CURVETO; + } + } - Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np); - bool const closed = (b->code == NR_MOVETO); + if (pit->closed()) { + // Add last knot (because sp_nodepath_subpath_close kills the last knot) + /* 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(); + // 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()) ) { + Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d; + sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos); + } - pos = NR::Point(b->x3, b->y3) * np->i2d; - if (b[1].code == NR_CURVETO) { - npos = NR::Point(b[1].x1, b[1].y1) * np->i2d; - } else { - npos = pos; - } - Inkscape::NodePath::Node *n; - n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos); - g_assert(sp->first == n); - g_assert(sp->last == n); - - b++; - t++; - while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) { - pos = NR::Point(b->x3, b->y3) * np->i2d; - if (b->code == NR_CURVETO) { - ppos = NR::Point(b->x2, b->y2) * np->i2d; - } else { - ppos = pos; - } - if (b[1].code == NR_CURVETO) { - npos = NR::Point(b[1].x1, b[1].y1) * np->i2d; - } else { - npos = pos; + sp_nodepath_subpath_close(sp); } - n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos); - b++; - t++; } - - if (closed) sp_nodepath_subpath_close(sp); - - return b; } /** - * Convert from sodipodi:nodetypes to new style type string. + * Convert from sodipodi:nodetypes to new style type array. */ -static gchar *parse_nodetypes(gchar const *types, gint length) +static +Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length) { - g_assert(length > 0); + Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1]; - gchar *typestr = g_new(gchar, length + 1); - - gint pos = 0; + guint pos = 0; if (types) { - for (gint i = 0; types[i] && ( i < length ); i++) { + for (guint i = 0; types[i] && ( i < length ); i++) { while ((types[i] > '\0') && (types[i] <= ' ')) i++; if (types[i] != '\0') { switch (types[i]) { 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; @@ -520,7 +623,9 @@ static gchar *parse_nodetypes(gchar const *types, gint length) } } - while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE; + while (pos < length) { + typestr[pos++] = Inkscape::NodePath::NODE_NONE; + } return typestr; } @@ -540,10 +645,17 @@ static void update_object(Inkscape::NodePath::Path *np) if (np->show_helperpath) { SPCurve * helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d ); + helper_curve->transform(np->i2d); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve); helper_curve->unref(); } + + // updating helperpaths of LPEItems is now done in sp_lpe_item_update(); + //sp_nodepath_update_helperpaths(np); + + // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too + // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder! + np->shape_editor->update_knotholder(); } /** @@ -559,7 +671,7 @@ static void update_repr_internal(Inkscape::NodePath::Path *np) np->curve = create_curve(np); gchar *typestr = create_typestr(np); - gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve)); + gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector()); // determine if path has an effect applied and write to correct "d" attribute. if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed @@ -577,11 +689,14 @@ static void update_repr_internal(Inkscape::NodePath::Path *np) if (np->show_helperpath) { SPCurve * helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d ); + helper_curve->transform(np->i2d); sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve); helper_curve->unref(); } - } + + // TODO: do we need this call here? after all, update_object() should have been called just before + //sp_nodepath_update_helperpaths(np); +} /** * Update XML path node with data from path object, commit changes forever. @@ -591,11 +706,6 @@ void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotati //fixme: np can be NULL, so check before proceeding g_return_if_fail(np != NULL); - if (np->livarot_path) { - delete np->livarot_path; - np->livarot_path = NULL; - } - update_repr_internal(np); sp_canvas_end_forced_full_redraws(np->desktop->canvas); @@ -608,11 +718,6 @@ void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotati */ static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation) { - if (np->livarot_path) { - delete np->livarot_path; - np->livarot_path = NULL; - } - update_repr_internal(np); sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, annotation); @@ -636,7 +741,7 @@ static void stamp_repr(Inkscape::NodePath::Path *np) SPCurve *curve = create_curve(np); gchar *typestr = create_typestr(np); - gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve)); + gchar *svgpath = sp_svg_write_path(curve->get_pathvector()); new_repr->setAttribute(np->repr_key, svgpath); new_repr->setAttribute(np->repr_nodetypes_key, typestr); @@ -664,10 +769,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); @@ -726,6 +834,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; @@ -760,26 +871,25 @@ static gchar *create_typestr(Inkscape::NodePath::Path *np) return typestr; } -/** - * Returns current path in context. // later eliminate this function at all! - */ -static Inkscape::NodePath::Path *sp_nodepath_current() +// Returns different message contexts depending on the current context. This function should only +// be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all +// other cases. +static Inkscape::MessageContext * +get_message_context(SPEventContext *ec) { - if (!SP_ACTIVE_DESKTOP) { - return NULL; - } - - SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context; + Inkscape::MessageContext *mc = 0; - if (!SP_IS_NODE_CONTEXT(event_context)) { - return NULL; + if (SP_IS_NODE_CONTEXT(ec)) { + mc = SP_NODE_CONTEXT(ec)->_node_message_context; + } else if (SP_IS_LPETOOL_CONTEXT(ec)) { + mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context; + } else { + g_warning ("Nodepath should only be present in Node tool or Geometric tool."); } - return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath(); + return mc; } - - /** \brief Fills node and handle positions for three nodes, splitting line marked by end at distance t. @@ -802,16 +912,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; @@ -859,34 +969,43 @@ static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node g_assert(node->subpath); g_assert(g_list_find(node->subpath->nodes, node)); - Inkscape::NodePath::SubPath *sp = node->subpath; + Inkscape::NodePath::Node* result = 0; + Inkscape::NodePath::SubPath *sp = node->subpath; Inkscape::NodePath::Path *np = sp->nodepath; if (sp->closed) { sp_nodepath_subpath_open(sp, node); - return sp->first; - } else { + result = sp->first; + } else if ( (node == sp->first) || (node == sp->last ) ){ // no break for end nodes - if (node == sp->first) return NULL; - if (node == sp->last ) return NULL; - + result = 0; + } else { // create a new subpath - Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np); + Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np); // duplicate the break node as start of the new subpath - Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos); - - while (node->n.other) { // copy the remaining nodes into the new subpath - Inkscape::NodePath::Node *n = node->n.other; - Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos); - if (n->selected) { - sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection - } - sp_nodepath_node_destroy(n); // remove the point on the original subpath + Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, + static_cast(node->type), + NR_MOVETO, &node->pos, &node->pos, &node->n.pos); + + // attach rest of curve to new node + g_assert(node->n.other); + newnode->n.other = node->n.other; node->n.other = NULL; + newnode->n.other->p.other = newnode; + newsubpath->last = sp->last; + sp->last = node; + node = newnode; + while (node->n.other) { + node = node->n.other; + node->subpath = newsubpath; + sp->nodes = g_list_remove(sp->nodes, node); + newsubpath->nodes = g_list_prepend(newsubpath->nodes, node); } - return newnode; + + result = newnode; } + return result; } /** @@ -907,10 +1026,11 @@ static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath:: Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos); - if (!node->n.other || !node->p.other) // if node is an endnode, select it + if (!node->n.other || !node->p.other) { // if node is an endnode, select it return node; - else + } else { return newnode; // otherwise select the newly created node + } } static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node) @@ -932,34 +1052,64 @@ static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode g_assert(end->subpath); g_assert(end->p.other); - if (end->code == static_cast< guint > ( code ) ) - return; + if (end->code != static_cast(code) ) { + Inkscape::NodePath::Node *start = end->p.other; - Inkscape::NodePath::Node *start = end->p.other; + end->code = code; - end->code = code; - - if (code == NR_LINETO) { - if (start->code == NR_LINETO) { - sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP); - } - if (end->n.other) { - if (end->n.other->code == NR_LINETO) { - sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP); + if (code == NR_LINETO) { + if (start->code == NR_LINETO) { + sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP); } + if (end->n.other) { + if (end->n.other->code == NR_LINETO) { + 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 { + 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); + sp_node_adjust_handle(end, -1); } - } else { - NR::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); - sp_node_adjust_handle(end, -1); + + sp_node_update_handles(start); + sp_node_update_handles(end); } +} - sp_node_update_handles(start); - sp_node_update_handles(end); +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. */ @@ -976,15 +1126,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)) { @@ -1005,6 +1147,7 @@ static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::N bool sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side) { +// TODO clean up multiple returns Inkscape::NodePath::Node *othernode = side->other; if (!othernode) return false; @@ -1016,28 +1159,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) { @@ -1051,18 +1201,22 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod // pull opposite handle in line with the existing one } } else if (no_handles) { - if (both_segments_are_lines OR both_segments_are_curves) { + if (both_segments_are_lines + OR both_segments_are_curves + OR one_is_line_but_the_curveside_node_is_selected_and_has_two_handles) { //pull both handles } else { // pull the handle opposite to line segment, making node half-smooth } } */ - 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); +#define NODE_HAS_BOTH_HANDLES(node) ((Geom::L2(node->pos - node->n.pos) > 1e-6) && (Geom::L2(node->pos - node->p.pos) > 1e-6)) + if (p_has_handle && n_has_handle) { // do nothing, adjust_handles will line them up } else if (p_has_handle || n_has_handle) { @@ -1071,6 +1225,7 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod Radial handle (node->pos - node->p.pos); if (fabs(line.a - handle.a) < 1e-3) { // lined up // already half-smooth; pull opposite handle too making it fully smooth + node->n.other->code = NR_CURVETO; node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3; } else { // do nothing, adjust_handles will line the handle up, producing a half-smooth node @@ -1080,6 +1235,7 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod Radial handle (node->pos - node->n.pos); if (fabs(line.a - handle.a) < 1e-3) { // lined up // already half-smooth; pull opposite handle too making it fully smooth + node->code = NR_CURVETO; node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3; } else { // do nothing, adjust_handles will line the handle up, producing a half-smooth node @@ -1088,63 +1244,46 @@ 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)) { - // no handles, but both segments are either lnes or curves: - //pull both handles + if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other) || + (n_is_line && node->p.other && node->p.other->selected && NODE_HAS_BOTH_HANDLES(node->p.other)) || + (p_is_line && node->n.other && node->n.other->selected && NODE_HAS_BOTH_HANDLES(node->n.other)) + ) { + // no handles, but: both segments are either lines or curves; or: one is line and the + // node at the other side is selected (so it was just smoothed too!) and now has both + // handles: then pull both handles here // convert both to curves: 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); } } } @@ -1161,30 +1300,43 @@ 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; if (node->p.other) { - if (node->code == NR_LINETO) { + if (sp_node_side_is_line(node, &node->p)) { sp_node_adjust_handle(node, 1); 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) { + if (sp_node_side_is_line(node, &node->n)) { sp_node_adjust_handle(node, -1); 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 @@ -1201,47 +1353,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 = true) +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; + Geom::Point delta(dx, dy); + Geom::Point best_pt = delta; + Inkscape::SnappedPoint best; - - if (snap) { + 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(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, SP_PATH(n->subpath->nodepath->item), &unselected_nodes); - Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta); - if (s.getDistance() < best) { - best = s.getDistance(); - best_abs = s; - best_pt = 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_snaptarget(); } } @@ -1262,34 +1448,42 @@ near x = 0. double sculpt_profile (double x, double alpha, guint profile) { - if (x >= 1) - return 0; - if (x <= 0) - return 1; + double result = 1; - switch (profile) { - case SCULPT_PROFILE_LINEAR: - return 1 - x; - case SCULPT_PROFILE_BELL: - return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5); - case SCULPT_PROFILE_ELLIPTIC: - return sqrt(1 - x*x); + if (x >= 1) { + result = 0; + } else if (x <= 0) { + result = 1; + } else { + switch (profile) { + case SCULPT_PROFILE_LINEAR: + result = 1 - x; + break; + case SCULPT_PROFILE_BELL: + result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5); + break; + case SCULPT_PROFILE_ELLIPTIC: + result = sqrt(1 - x*x); + break; + default: + g_assert_not_reached(); + } } - return 1; + return result; } 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; @@ -1303,7 +1497,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); @@ -1319,7 +1513,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: @@ -1390,8 +1585,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; @@ -1407,8 +1602,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; @@ -1420,7 +1615,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 @@ -1431,7 +1626,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)); } } } @@ -1444,9 +1639,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); } @@ -1485,11 +1680,10 @@ sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy * Move node selection off screen and commit the change. */ void -sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy) +sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy) { // borrowed from sp_selection_move_screen in selection-chemistry.c // we find out the current zoom factor and divide deltas by it - SPDesktop *desktop = SP_ACTIVE_DESKTOP; gdouble zoom = desktop->current_zoom(); gdouble zdx = dx / zoom; @@ -1511,11 +1705,11 @@ sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdo /** * Move selected nodes to the absolute position given */ -void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis) +void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis) { for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]); + Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]); sp_node_moveto(n, npos); } @@ -1523,17 +1717,17 @@ void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coor } /** - * 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 */ -NR::Maybe sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis) +boost::optional sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis) { - NR::Maybe no_coord = NR::Nothing(); + boost::optional no_coord; g_return_val_if_fail(nodepath->selected, no_coord); // 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 @@ -1546,7 +1740,7 @@ NR::Maybe sp_node_selected_common_coord (Inkscape::NodePath::Path *no if (coincide) { return coord; } else { - NR::Rect bbox = sp_node_selected_bbox(nodepath); + Geom::Rect bbox = sp_node_selected_bbox(nodepath); // currently we return the coordinate of the bounding box midpoint because I don't know how // to erase the spin button entry field :), but maybe this can be useful behaviour anyway return bbox.midpoint()[axis]; @@ -1590,7 +1784,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 @@ -1602,11 +1796,11 @@ static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gb sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos); sp_knot_show(side->knot); } else { - if (side->knot->pos != side->pos) { // only if it's really moved + if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved if (fire_move_signals) { - sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well + sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well } else { - sp_knot_moveto(side->knot, &side->pos); + sp_knot_moveto(side->knot, side->pos); sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos); } } @@ -1641,11 +1835,11 @@ static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_mov sp_knot_show(node->knot); } - if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update + if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update if (fire_move_signals) - sp_knot_set_position(node->knot, &node->pos, 0); + sp_knot_set_position(node->knot, node->pos, 0); else - sp_knot_moveto(node->knot, &node->pos); + sp_knot_moveto(node->knot, node->pos); } gboolean show_handles = node->selected; @@ -1690,10 +1884,10 @@ static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath) void sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show) { - if (nodepath == NULL) return; - - nodepath->show_handles = show; - sp_nodepath_update_handles(nodepath); + if (nodepath) { + nodepath->show_handles = show; + sp_nodepath_update_handles(nodepath); + } } /** @@ -1708,7 +1902,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; @@ -1718,7 +1912,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) { @@ -1734,9 +1928,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]) {} @@ -1750,7 +1944,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; @@ -1783,7 +1977,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; @@ -1839,21 +2033,33 @@ 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; } - sp_nodepath_ensure_livarot_path(nodepath); - NR::Maybe maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p); - if (!maybe_position) { + SPCurve *curve = create_curve(nodepath); // perhaps we can use nodepath->curve here instead? + Geom::PathVector const &pathv = curve->get_pathvector(); + boost::optional pvpos = Geom::nearestPoint(pathv, p); + if (!pvpos) { + g_print ("Possible error?\n"); return; } - Path::cut_position position = *maybe_position; + + // calculate index for nodepath's representation. + unsigned int segment_index = floor(pvpos->t) + 1; + for (unsigned int i = 0; i < pvpos->path_nr; ++i) { + segment_index += pathv[i].size() + 1; + if (pathv[i].closed()) { + segment_index += 1; + } + } + + curve->unref(); //find segment to segment - Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece); + Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index); //fixme: this can return NULL, so check before proceeding. g_return_if_fail(e != NULL); @@ -1875,27 +2081,45 @@ 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; } - sp_nodepath_ensure_livarot_path(nodepath); - NR::Maybe maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p); - if (!maybe_position) { + SPCurve *curve = create_curve(nodepath); // perhaps we can use nodepath->curve here instead? + Geom::PathVector const &pathv = curve->get_pathvector(); + boost::optional pvpos = Geom::nearestPoint(pathv, p); + if (!pvpos) { + g_print ("Possible error?\n"); return; } - Path::cut_position position = *maybe_position; + + // calculate index for nodepath's representation. + double int_part; + double t = std::modf(pvpos->t, &int_part); + unsigned int segment_index = (unsigned int)int_part + 1; + for (unsigned int i = 0; i < pvpos->path_nr; ++i) { + segment_index += pathv[i].size() + 1; + if (pathv[i].closed()) { + segment_index += 1; + } + } + + curve->unref(); //find segment to split - Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece); + 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) { - position.t = 1.0 - position.t; + t = 1.0 - t; } - Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t); + + Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t); sp_nodepath_node_select(n, FALSE, TRUE); /* fixme: adjust ? */ @@ -1914,14 +2138,23 @@ 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(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(node); + Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node); //fixme: e and e->p can be NULL, so check for those before proceeding 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 @@ -1941,8 +2174,8 @@ sp_nodepath_curve_drag(int node, double t, NR::Point delta) 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; @@ -1965,13 +2198,15 @@ void sp_node_selected_break(Inkscape::NodePath::Path *nodepath) { if (!nodepath) return; + GList *tempin = g_list_copy(nodepath->selected); GList *temp = NULL; - for (GList *l = nodepath->selected; l != NULL; l = l->next) { + for (GList *l = tempin; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n); if (nn == NULL) continue; // no break, no new node temp = g_list_prepend(temp, nn); } + g_list_free(tempin); if (temp) { sp_nodepath_deselect(nodepath); @@ -2022,7 +2257,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)) { @@ -2045,7 +2280,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) { @@ -2079,13 +2314,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); @@ -2136,7 +2371,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; @@ -2272,7 +2507,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) { @@ -2283,15 +2518,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); @@ -2306,16 +2541,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, @@ -2363,7 +2598,7 @@ void sp_node_delete_preserve(GList *nodes_to_delete) //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to //delete this nodepath's object, not the entire selection! (though at this time, this //does not matter) - sp_selection_delete(); + sp_selection_delete(nodepath->desktop); sp_document_done (document, SP_VERB_CONTEXT_NODE, _("Delete nodes")); } else { @@ -2399,7 +2634,7 @@ void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath) if (nodepath->subpaths == NULL || sp_nodepath_get_node_count(nodepath) < 2) { SPDocument *document = sp_desktop_document (nodepath->desktop); - sp_selection_delete(); + sp_selection_delete(nodepath->desktop); sp_document_done (document, SP_VERB_CONTEXT_NODE, _("Delete nodes")); return; @@ -2615,12 +2850,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); @@ -2854,7 +3089,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); @@ -2988,13 +3223,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; } } @@ -3035,19 +3270,17 @@ sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath) */ GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath) { - if (!nodepath->selected) { - return NULL; - } - GList *r = NULL; - guint i = 0; - 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; - i++; - if (node->selected) { - r = g_list_append(r, GINT_TO_POINTER(i)); + if (nodepath->selected) { + guint i = 0; + 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; + i++; + if (node->selected) { + r = g_list_append(r, GINT_TO_POINTER(i)); + } } } } @@ -3082,6 +3315,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); @@ -3104,9 +3341,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; @@ -3114,14 +3351,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; } @@ -3141,6 +3378,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; @@ -3152,7 +3394,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; @@ -3161,14 +3403,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. */ @@ -3272,6 +3538,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; @@ -3301,6 +3571,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); } @@ -3321,7 +3593,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; @@ -3330,6 +3602,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); @@ -3344,6 +3619,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")); @@ -3356,13 +3632,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]; } } @@ -3371,11 +3647,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])); } /** @@ -3383,30 +3659,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; + 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 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; @@ -3430,8 +3706,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; @@ -3446,7 +3722,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! @@ -3455,7 +3731,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; } @@ -3464,28 +3740,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 @@ -3518,7 +3794,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); @@ -3539,25 +3815,34 @@ 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 - sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]); + 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[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]); + sp_nodepath_selected_nodes_move(n->subpath->nodepath, + n->origin[Geom::X] - n->pos[Geom::X], + p[Geom::Y] - n->pos[Geom::Y], + true, + 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); } } @@ -3598,6 +3883,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); } @@ -3624,10 +3915,10 @@ static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data) // forget origin and set knot position once more (because it can be wrong now due to restrictions) if (n->p.knot == knot) { n->p.origin_radial.a = 0; - sp_knot_set_position(knot, &n->p.pos, state); + sp_knot_set_position(knot, n->p.pos, state); } else if (n->n.knot == knot) { n->n.origin_radial.a = 0; - sp_knot_set_position(knot, &n->n.pos, state); + sp_knot_set_position(knot, n->n.pos, state); } else { g_assert_not_reached(); } @@ -3638,7 +3929,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; @@ -3660,31 +3951,44 @@ static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, SPDesktop *desktop = n->subpath->nodepath->desktop; SnapManager &m = desktop->namedview->snap_manager; - m.setup(desktop, n->subpath->nodepath->item); - Inkscape::SnappedPoint s ; + 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_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::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false); } - s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta)); } else { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p); + if ((state & GDK_SHIFT_MASK) == 0) { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type); + } } } else { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p); + if ((state & GDK_SHIFT_MASK) == 0) { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type); + } } - - *p = s.getPoint(); + + s.getPoint(p); sp_node_adjust_handle(n, -which); @@ -3694,9 +3998,10 @@ static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, /** * 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; @@ -3715,10 +4020,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. @@ -3738,13 +4043,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); @@ -3764,23 +4069,23 @@ 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); + 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: // we cannot emit a "moved" signal because we're now processing it - sp_knot_moveto(me->knot, &(me->pos)); + sp_knot_moveto(me->knot, me->pos); update_object(n->subpath->nodepath); @@ -3789,13 +4094,15 @@ static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer if (!desktop) return; SPEventContext *ec = desktop->event_context; if (!ec) return; - Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context; + + Inkscape::MessageContext *mc = get_message_context(ec); + if (!mc) return; 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()); @@ -3828,12 +4135,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: @@ -3892,8 +4199,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); @@ -3934,10 +4241,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, @@ -3959,7 +4266,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 @@ -3969,22 +4276,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; @@ -4006,8 +4313,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); @@ -4072,10 +4379,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, @@ -4098,24 +4405,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; @@ -4138,7 +4455,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, NR::Maybe center) +void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional center) { if (!nodepath || !nodepath->selected) return; @@ -4152,14 +4469,14 @@ void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Ma } else { // scale nodes as an "object": - NR::Rect box = sp_node_selected_bbox (nodepath); + Geom::Rect box = sp_node_selected_bbox (nodepath); 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; @@ -4173,12 +4490,12 @@ void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Ma sp_nodepath_update_repr(nodepath, _("Flip nodes")); } -NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath) +Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath) { g_assert (nodepath->selected); 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 @@ -4280,7 +4597,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); @@ -4299,7 +4616,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 { @@ -4340,14 +4657,13 @@ sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node * n->n.other = next; 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); + 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); @@ -4452,19 +4768,19 @@ static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node) static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which) { g_assert(node); - + Inkscape::NodePath::NodeSide * result = 0; switch (which) { case -1: - return &node->p; + result = &node->p; + break; case 1: - return &node->n; - default: + result = &node->n; break; + default: + g_assert_not_reached(); } - g_assert_not_reached(); - - return NULL; + return result; } /** @@ -4473,13 +4789,17 @@ static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node * static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me) { g_assert(node); + Inkscape::NodePath::NodeSide *result = 0; - if (me == &node->p) return &node->n; - if (me == &node->n) return &node->p; - - g_assert_not_reached(); + if (me == &node->p) { + result = &node->n; + } else if (me == &node->n) { + result = &node->p; + } else { + g_assert_not_reached(); + } - return NULL; + return result; } /** @@ -4489,30 +4809,34 @@ static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Ink { g_assert(node); + NRPathcode result = NR_END; if (me == &node->p) { - if (node->p.other) return (NRPathcode)node->code; - return NR_MOVETO; - } - - if (me == &node->n) { - if (node->n.other) return (NRPathcode)node->n.other->code; - return NR_MOVETO; + if (node->p.other) { + result = (NRPathcode)node->code; + } else { + result = NR_MOVETO; + } + } else if (me == &node->n) { + if (node->n.other) { + result = (NRPathcode)node->n.other->code; + } else { + result = NR_MOVETO; + } + } else { + g_assert_not_reached(); } - g_assert_not_reached(); - - return NR_END; + return result; } /** * Return node with the given index */ Inkscape::NodePath::Node * -sp_nodepath_get_node_by_index(int index) +sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index) { Inkscape::NodePath::Node *e = NULL; - Inkscape::NodePath::Path *nodepath = sp_nodepath_current(); if (!nodepath) { return e; } @@ -4556,7 +4880,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; @@ -4574,6 +4898,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"); } @@ -4610,12 +4936,13 @@ sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to Sha if (nodepath) { desktop = nodepath->desktop; } else { - desktop = SP_ACTIVE_DESKTOP; + desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above } SPEventContext *ec = desktop->event_context; if (!ec) return; - Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context; + + Inkscape::MessageContext *mc = get_message_context(ec); if (!mc) return; inkscape_active_desktop()->emitToolSubselectionChanged(NULL); @@ -4677,12 +5004,10 @@ SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) { } else if ( IS_LIVEPATHEFFECT(object) && key) { const gchar *svgd = object->repr->attribute(key); if (svgd) { - NArtBpath *bpath = sp_svg_read_path(svgd); - SPCurve *curve_new = SPCurve::new_from_bpath(bpath); + Geom::PathVector pv = sp_svg_read_pathv(svgd); + SPCurve *curve_new = new SPCurve(pv); if (curve_new) { curve = curve_new; // don't do curve_copy because curve_new is already only created for us! - } else { - g_free(bpath); } } } @@ -4701,23 +5026,83 @@ 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) ) { - // FIXME: this writing to string and then reading from string is bound to be slow. - // create a method to convert from curve directly to 2geom... - gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve)); - LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath); - g_free(svgpath); + 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); + } + } + } +} - 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 Geom::Matrix & i2d, guint32 color = 0xff0000ff) { + SPCurve *flash_curve = curve->copy(); + 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... + 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; +} + +SPCanvasItem * +sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) { + if (!item || !desktop) { + return NULL; + } + + 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; if (show) { SPCurve *helper_curve = np->curve->copy(); - helper_curve->transform(np->i2d ); + helper_curve->transform(np->i2d); if (!np->helper_path) { + //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!! + np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve); sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO); @@ -4749,7 +5134,6 @@ void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) { // coding tip: search for this text : "Make selected segments lines" } - /* Local Variables: mode:c++