X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fnodepath.cpp;h=34c850d086099b87a8bf33c85226ebc46ef18de8;hb=6c3e745a94ef6b25a4ef9f018d350a7535aa45af;hp=d7ca69112257d1e611070d603d5e95a90ab6555b;hpb=235e8e7977f17c6070b4691837b8a6cb2bd4cc35;p=inkscape.git diff --git a/src/nodepath.cpp b/src/nodepath.cpp index d7ca69112..34c850d08 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 <2geom/pathvector.h> -#include <2geom/sbasis-to-bezier.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,30 +37,32 @@ #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 "libnr/nr-matrix-ops.h" -#include "splivarot.h" #include "svg/svg.h" #include "verbs.h" #include "display/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! @@ -96,8 +101,6 @@ static GMemChunk *nodechunk = NULL; /* Creation from object */ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t); -static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, - Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode ); static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length); /* Object updating */ @@ -117,19 +120,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 const &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 */ @@ -139,7 +143,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 */ @@ -154,22 +158,95 @@ 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 void sp_nodepath_draw_helper_curve(Inkscape::NodePath::Path *np, SPDesktop *desktop) { - // Draw helper curve - if (np->show_helperpath) { - SPCurve *helper_curve = np->curve->copy(); - helper_curve->transform(to_2geom(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(); +static SPCanvasItem * +sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) { + SPCurve *helper_curve = curve->copy(); + helper_curve->transform(np->i2d); + SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_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 SPCanvasItem * +canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) { + SPCurve *helper_curve = new SPCurve(pathv); + return sp_nodepath_make_helper_item(np, helper_curve, show); +} + +static void +sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) { + //std::map > helper_path_vec; + 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) { + np->helper_path_vec[lpe].push_back(canvasitem_from_pathvec(np, *j, true)); + } + } + } +} + +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); + for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) { + Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe(); + if (lpe) { + /* update canvas items from the effect's helper paths; note that this code relies on the + * fact that getHelperPaths() will always return the same number of helperpaths in the same + * order as during their creation in sp_nodepath_create_helperpaths + */ + std::vector hpaths = lpe->getHelperPaths(lpeitem); + for (unsigned int j = 0; j < hpaths.size(); ++j) { + SPCurve *curve = new SPCurve(hpaths[j]); + curve->transform(np->i2d); + sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve); + curve = curve->unref(); + } + } + } +} + +static void +sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) { + for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) { + for (std::vector::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) { + GtkObject *temp = *j; + *j = NULL; + gtk_object_destroy(temp); + } + } + np->helper_path_vec.clear(); +} + + /** * \brief Creates new nodepath from item + * + * \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) { @@ -194,8 +271,9 @@ 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; + } if (curve->get_segment_count() < 1) { curve->unref(); @@ -203,26 +281,27 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, } //Create new nodepath - Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1); + Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path(); if (!np) { curve->unref(); return NULL; } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + // Set defaults np->desktop = desktop; np->object = object; 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()) { @@ -241,14 +320,19 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, // to a change in repr by regenerating nodepath --bb sp_object_read_attr(SP_OBJECT(np->item), "transform"); - np->i2d = from_2geom(sp_item_i2d_affine(np->item)); + np->i2d = sp_item_i2d_affine(np->item); 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 (this is a dirty check, hopefully nobody tries feeding non-lpeobjects into this method with non-null repr_key_in) 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); } @@ -269,9 +353,10 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, /* 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 const &pathv = curve->get_pathvector(); - guint length = curve->get_segment_count(); - for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) { + 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; } @@ -279,7 +364,7 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length); // create the subpath(s) from the bpath - subpaths_from_pathvector(np, pathv, typestr); + 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); @@ -287,10 +372,12 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, 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) { + np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true); + } - sp_nodepath_draw_helper_curve(np, desktop); + sp_nodepath_create_helperpaths(np); return np; } @@ -298,74 +385,52 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, /** * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it. */ -void sp_nodepath_destroy(Inkscape::NodePath::Path *np) { - - if (!np) //soft fail, like delete - return; - - while (np->subpaths) { - sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data); +Inkscape::NodePath::Path::~Path() { + while (this->subpaths) { + sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data); } //Inform the ShapeEditor that made me, if any, that I am gone. - if (np->shape_editor) - np->shape_editor->nodepath_destroyed(); + if (this->shape_editor) + this->shape_editor->nodepath_destroyed(); - g_assert(!np->selected); - - 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); - np->livarot_path = new Path; - np->livarot_path->LoadPathVector(curve->get_pathvector()); - - 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; } @@ -374,12 +439,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; } @@ -389,9 +454,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; } /** @@ -399,9 +466,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; } /** @@ -409,24 +478,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; } /** @@ -468,11 +537,30 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np); - NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d; + Geom::Point ppos = pit->initialPoint() * np->i2d; NRPathcode pcode = NR_MOVETO; + /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) { - add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode); + 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; + } } if (pit->closed()) { @@ -480,8 +568,9 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already * If the length is zero, don't add it to the nodepath. */ Geom::Curve const &closing_seg = pit->back_closed(); - if ( ! closing_seg.isDegenerate() ) { - NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d; + // 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); } @@ -489,37 +578,6 @@ static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVec } } } -// should add initial point of curve with type of previous curve: -static void add_curve_to_subpath(Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, Inkscape::NodePath::NodeType const *t, guint & i, - NR::Point & ppos, NRPathcode & pcode) -{ - if( dynamic_cast(&c) || - dynamic_cast(&c) || - dynamic_cast(&c) ) - { - NR::Point pos = from_2geom(c.initialPoint()) * np->i2d; - sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos); - ppos = from_2geom(c.finalPoint()); - pcode = NR_LINETO; - } - else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast(&c)) { - std::vector points = cubic_bezier->points(); - NR::Point pos = from_2geom(points[0]) * np->i2d; - NR::Point npos = from_2geom(points[1]) * np->i2d; - sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos); - ppos = from_2geom(points[2]) * np->i2d; - pcode = NR_CURVETO; - } - else { - //this case handles sbasis as well as all other curve types - Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1); - - for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) { - add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode); - } - } -} - /** * Convert from sodipodi:nodetypes to new style type array. @@ -539,6 +597,9 @@ Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length) case 's': typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH; break; + case 'a': + typestr[pos++] =Inkscape::NodePath::NODE_AUTO; + break; case 'z': typestr[pos++] =Inkscape::NodePath::NODE_SYMM; break; @@ -553,7 +614,9 @@ Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length) } } - while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE; + while (pos < length) { + typestr[pos++] = Inkscape::NodePath::NODE_NONE; + } return typestr; } @@ -573,11 +636,14 @@ static void update_object(Inkscape::NodePath::Path *np) if (np->show_helperpath) { SPCurve * helper_curve = np->curve->copy(); - helper_curve->transform(to_2geom(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(); @@ -614,11 +680,14 @@ static void update_repr_internal(Inkscape::NodePath::Path *np) if (np->show_helperpath) { SPCurve * helper_curve = np->curve->copy(); - helper_curve->transform(to_2geom(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. @@ -628,11 +697,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); @@ -645,11 +709,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); @@ -701,10 +760,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); @@ -763,6 +825,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; @@ -797,26 +862,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. @@ -839,16 +903,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; @@ -896,22 +960,24 @@ 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); + 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); @@ -928,8 +994,9 @@ static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node } - return newnode; + result = newnode; } + return result; } /** @@ -950,10 +1017,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) @@ -975,34 +1043,61 @@ 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; + + 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. */ @@ -1019,15 +1114,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)) { @@ -1048,6 +1135,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; @@ -1063,8 +1151,8 @@ sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSi 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); + (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 && + Geom::L2(node->pos - side->pos) < 1e-6); return is_line; } @@ -1076,6 +1164,13 @@ sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSi */ 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) { /* @@ -1101,8 +1196,8 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod } } */ - bool p_has_handle = (NR::L2(node->pos - node->p.pos) > 1e-6); - bool n_has_handle = (NR::L2(node->pos - node->n.pos) > 1e-6); + bool p_has_handle = (Geom::L2(node->pos - node->p.pos) > 1e-6); + bool n_has_handle = (Geom::L2(node->pos - node->n.pos) > 1e-6); bool p_is_line = sp_node_side_is_line(node, &node->p); bool n_is_line = sp_node_side_is_line(node, &node->n); @@ -1131,16 +1226,16 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod // pull n handle node->n.other->code = NR_CURVETO; double len = (type == Inkscape::NodePath::NODE_SYMM)? - NR::L2(node->p.pos - node->pos) : - NR::L2(node->n.other->pos - node->pos) / 3; - node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos); + Geom::L2(node->p.pos - node->pos) : + Geom::L2(node->n.other->pos - node->pos) / 3; + node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos); } else if (n_has_handle && node->p.other) { // pull p handle node->code = NR_CURVETO; double len = (type == Inkscape::NodePath::NODE_SYMM)? - NR::L2(node->n.pos - node->pos) : - NR::L2(node->p.other->pos - node->pos) / 3; - node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos); + Geom::L2(node->n.pos - node->pos) : + Geom::L2(node->p.other->pos - node->pos) / 3; + node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos); } } else if (!p_has_handle && !n_has_handle) { if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) { @@ -1151,43 +1246,22 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod node->code = NR_CURVETO; node->n.other->code = NR_CURVETO; - NR::Point leg_prev = node->pos - node->p.other->pos; - NR::Point leg_next = node->pos - node->n.other->pos; - - double norm_leg_prev = L2(leg_prev); - double norm_leg_next = L2(leg_next); - - NR::Point delta; - if (norm_leg_next > 0.0) { - delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev; - (&delta)->normalize(); - } - - if (type == Inkscape::NodePath::NODE_SYMM) { - double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2; - node->p.pos = node->pos + 0.3 * norm_leg_avg * delta; - node->n.pos = node->pos - 0.3 * norm_leg_avg * delta; - } else { - // length of handle is proportional to distance to adjacent node - node->p.pos = node->pos + 0.3 * norm_leg_prev * delta; - node->n.pos = node->pos - 0.3 * norm_leg_next * delta; - } - + sp_node_adjust_handles_auto(node); } else { // pull the handle opposite to line segment, making it half-smooth if (p_is_line && node->n.other) { if (type != Inkscape::NodePath::NODE_SYMM) { // pull n handle node->n.other->code = NR_CURVETO; - double len = NR::L2(node->n.other->pos - node->pos) / 3; - node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos); + double len = Geom::L2(node->n.other->pos - node->pos) / 3; + node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos); } } else if (n_is_line && node->p.other) { if (type != Inkscape::NodePath::NODE_SYMM) { // pull p handle node->code = NR_CURVETO; - double len = NR::L2(node->p.other->pos - node->pos) / 3; - node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos); + double len = Geom::L2(node->p.other->pos - node->pos) / 3; + node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos); } } } @@ -1204,13 +1278,18 @@ void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::Nod /** * Move node to point, and adjust its and neighbouring handles. */ -void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) +void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p) { - NR::Point delta = p - node->pos; - node->pos = p; + if (node->type == Inkscape::NodePath::NODE_AUTO) { + node->pos = p; + sp_node_adjust_handles_auto(node); + } else { + Geom::Point delta = p - node->pos; + node->pos = p; - node->p.pos += delta; - node->n.pos += delta; + node->p.pos += delta; + node->n.pos += delta; + } Inkscape::NodePath::Node *node_p = NULL; Inkscape::NodePath::Node *node_n = NULL; @@ -1221,6 +1300,10 @@ void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) sp_node_adjust_handle(node->p.other, -1); node_p = node->p.other; } + if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) { + sp_node_adjust_handles_auto(node->p.other); + node_p = node->p.other; + } } if (node->n.other) { if (node->n.other->code == NR_LINETO) { @@ -1228,6 +1311,10 @@ void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p) sp_node_adjust_handle(node->n.other, 1); node_n = node->n.other; } + if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) { + sp_node_adjust_handles_auto(node->n.other); + node_n = node->n.other; + } } // this function is only called from batch movers that will update display at the end @@ -1244,13 +1331,13 @@ 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, +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 = NR::Point()) + Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point()) { - NR::Coord best = NR_HUGE; - NR::Point delta(dx, dy); - NR::Point best_pt = delta; + Geom::Coord best = NR_HUGE; + Geom::Point delta(dx, dy); + Geom::Point best_pt = delta; Inkscape::SnappedPoint best_abs; if (snap) { @@ -1259,13 +1346,13 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, * must provide that information. */ // Build a list of the unselected nodes to which the snapper should snap - std::vector unselected_nodes; + 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(to_2geom(node->pos)); } } } @@ -1274,19 +1361,19 @@ static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, for (GList *l = nodepath->selected; l != NULL; l = l->next) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data; - m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes); + m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes); Inkscape::SnappedPoint s; if (constrained) { Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint; dedicated_constraint.setPoint(n->pos); - s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint); + s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint); } else { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta); + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta)); } if (s.getSnapped() && (s.getDistance() < best)) { best = s.getDistance(); best_abs = s; - best_pt = s.getPoint() - n->pos; + best_pt = from_2geom(s.getPoint()) - n->pos; } } @@ -1314,34 +1401,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; @@ -1355,7 +1450,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); @@ -1371,7 +1466,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: @@ -1442,8 +1538,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; @@ -1459,8 +1555,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; @@ -1472,7 +1568,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 @@ -1483,7 +1579,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)); } } } @@ -1496,9 +1592,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); } @@ -1537,11 +1633,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; @@ -1563,11 +1658,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); } @@ -1575,17 +1670,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 @@ -1598,7 +1693,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]; @@ -1642,7 +1737,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 @@ -1654,11 +1749,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); } } @@ -1693,11 +1788,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; @@ -1742,10 +1837,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); + } } /** @@ -1760,7 +1855,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; @@ -1770,7 +1865,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) { @@ -1786,9 +1881,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]) {} @@ -1802,7 +1897,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; @@ -1835,7 +1930,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; @@ -1891,21 +1986,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); @@ -1927,27 +2034,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 ? */ @@ -1966,14 +2091,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 @@ -1993,8 +2127,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; @@ -2076,7 +2210,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)) { @@ -2099,7 +2233,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) { @@ -2190,7 +2324,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; @@ -2326,7 +2460,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) { @@ -2337,15 +2471,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); @@ -2360,12 +2494,12 @@ 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; @@ -2417,7 +2551,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 { @@ -2453,7 +2587,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; @@ -2669,12 +2803,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); @@ -2908,7 +3042,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); @@ -3042,13 +3176,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; } } @@ -3089,19 +3223,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)); + } } } } @@ -3136,6 +3268,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); @@ -3158,9 +3294,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; @@ -3173,9 +3309,9 @@ static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adj 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; } @@ -3195,6 +3331,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; @@ -3206,7 +3347,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; @@ -3215,14 +3356,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. */ @@ -3326,6 +3491,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; @@ -3355,6 +3524,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); } @@ -3410,13 +3581,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]; } } @@ -3425,11 +3596,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])); } /** @@ -3437,14 +3608,14 @@ 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; @@ -3455,12 +3626,12 @@ node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data) ( ((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; @@ -3484,8 +3655,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; @@ -3500,7 +3671,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! @@ -3509,7 +3680,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; } @@ -3518,28 +3689,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 @@ -3572,7 +3743,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); @@ -3593,34 +3764,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 + if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor sp_nodepath_selected_nodes_move(n->subpath->nodepath, - (*p)[NR::X] - n->pos[NR::X], - n->origin[NR::Y] - n->pos[NR::Y], + p[Geom::X] - n->pos[Geom::X], + n->origin[Geom::Y] - n->pos[Geom::Y], true, - true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X])); + true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X])); } else { // snap to vert sp_nodepath_selected_nodes_move(n->subpath->nodepath, - n->origin[NR::X] - n->pos[NR::X], - (*p)[NR::Y] - n->pos[NR::Y], + n->origin[Geom::X] - n->pos[Geom::X], + p[Geom::Y] - n->pos[Geom::Y], true, - true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y])); + true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y])); } } } else { // move freely if (n->is_dragging) { if (state & GDK_MOD1_MASK) { // sculpt - sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin); + sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin); } else { sp_nodepath_selected_nodes_move(n->subpath->nodepath, - (*p)[NR::X] - n->pos[NR::X], - (*p)[NR::Y] - n->pos[NR::Y], + p[Geom::X] - n->pos[Geom::X], + p[Geom::Y] - n->pos[Geom::Y], (state & GDK_SHIFT_MASK) == 0); } } @@ -3661,6 +3832,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); } @@ -3687,10 +3864,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(); } @@ -3701,7 +3878,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 const &p, guint state, gpointer data) { Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data; @@ -3723,32 +3900,43 @@ 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_snappoint(); + } Inkscape::NodePath::Node *othernode = opposite->other; 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); + Geom::Point ptemp = p; 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; + ptemp = n->pos + (scal / linelen) * ndelta; + } + if ((state & GDK_SHIFT_MASK) == 0) { + s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, ptemp, Inkscape::Snapper::ConstraintLine(ptemp, ndelta)); } - 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); + } } } else { - s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p); + if ((state & GDK_SHIFT_MASK) == 0) { + s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p); + } } - s.getPoint(*p); - sp_node_adjust_handle(n, -which); return FALSE; @@ -3757,9 +3945,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; @@ -3778,10 +3967,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. @@ -3801,13 +3990,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); @@ -3831,19 +4020,19 @@ static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer && 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); @@ -3852,13 +4041,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()); @@ -3891,12 +4082,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: @@ -3955,8 +4146,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); @@ -3997,10 +4188,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, @@ -4022,7 +4213,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 @@ -4032,22 +4223,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; @@ -4069,8 +4260,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); @@ -4135,10 +4326,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, @@ -4161,24 +4352,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; @@ -4201,7 +4402,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; @@ -4215,14 +4416,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; @@ -4236,12 +4437,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 @@ -4343,7 +4544,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); @@ -4362,7 +4563,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 { @@ -4403,14 +4604,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); @@ -4515,19 +4715,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; } /** @@ -4536,13 +4736,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; } /** @@ -4552,30 +4756,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; } @@ -4619,7 +4827,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; @@ -4637,6 +4845,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"); } @@ -4673,12 +4883,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); @@ -4762,16 +4973,25 @@ 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( np->curve->get_pathvector() ); - LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath); - g_free(svgpath); - - np->object->requestModified(SP_OBJECT_MODIFIED_FLAG); + Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe(); + if (lpe) { + Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast( lpe->getParameter(np->repr_key) ); + if (pathparam) { + pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG + np->object->requestModified(SP_OBJECT_MODIFIED_FLAG); + } + } } } +/* +SPCanvasItem * +sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) { + return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path)); +} +*/ + +/* SPCanvasItem * sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) { SPCurve *flash_curve = curve->copy(); @@ -4789,17 +5009,39 @@ sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem SPCanvasItem * sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path), - prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff)); + prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff)); +} +*/ + +SPCanvasItem * +sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) { + SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy(); + Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path)); + flash_curve->transform(i2d); + SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve); + // would be nice if its color could be XORed or something, now it is invisible for red stroked objects... + // unless we also flash the nodes... + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff); + sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT); + sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO); + sp_canvas_item_show(canvasitem); + flash_curve->unref(); + return canvasitem; } +// 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(to_2geom(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); @@ -4831,7 +5073,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++