From aee6bba4d090adbd7801efd2eb156ca8aee2301f Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Fri, 8 Aug 2008 22:28:49 +0000 Subject: [PATCH] Snap to intersections of any kind of path (were we previously only could snap to intersections of line-segments) --- src/attributes-test.h | 2 +- src/attributes.cpp | 2 +- src/attributes.h | 2 +- src/object-snapper.cpp | 33 ++---- src/snap.cpp | 19 ++-- src/snap.h | 6 +- src/snapped-curve.cpp | 146 ++++++++++++++++++++++++++ src/snapped-curve.h | 55 ++++++++++ src/snapper.h | 2 + src/sp-namedview.cpp | 6 +- src/ui/dialog/document-properties.cpp | 6 +- 11 files changed, 233 insertions(+), 46 deletions(-) create mode 100644 src/snapped-curve.cpp create mode 100644 src/snapped-curve.h diff --git a/src/attributes-test.h b/src/attributes-test.h index c8287f769..25167e75b 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -353,7 +353,7 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:snap-guide", true}, {"inkscape:snap-center", true}, {"inkscape:snap-intersection-grid-guide", true}, - {"inkscape:snap-intersection-line-segments", true}, + {"inkscape:snap-intersection-paths", true}, {"inkscape:pageopacity", true}, {"inkscape:pageshadow", true}, {"inkscape:transform-center-x", true}, diff --git a/src/attributes.cpp b/src/attributes.cpp index c67144692..e64cedf36 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -93,7 +93,7 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_SNAP_GUIDE, "inkscape:snap-guide"}, {SP_ATTR_INKSCAPE_SNAP_CENTER, "inkscape:snap-center"}, {SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, "inkscape:snap-intersection-grid-guide"}, - {SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM, "inkscape:snap-intersection-line-segments"}, + {SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, "inkscape:snap-intersection-paths"}, {SP_ATTR_INKSCAPE_OBJECT_PATHS, "inkscape:object-paths"}, {SP_ATTR_INKSCAPE_OBJECT_NODES, "inkscape:object-nodes"}, {SP_ATTR_INKSCAPE_BBOX_PATHS, "inkscape:bbox-paths"}, diff --git a/src/attributes.h b/src/attributes.h index c1c4dcadc..82fba18a9 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -93,7 +93,7 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_SNAP_GUIDE, SP_ATTR_INKSCAPE_SNAP_CENTER, SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, - SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM, + SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, SP_ATTR_INKSCAPE_OBJECT_PATHS, SP_ATTR_INKSCAPE_OBJECT_NODES, SP_ATTR_INKSCAPE_BBOX_PATHS, diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 3bebc7b22..a3f7383db 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -416,6 +416,11 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, /* FIXME: this seems like a hack. Perhaps Snappers should be ** in SPDesktop rather than SPNamedView? */ + // TODO Diederik: shouldn't we just make all snapping code use document + // coordinates instead? Then we won't need a pointer to the desktop any longer + // At least we should define a clear boundary between those different coordinates, + // now this is not well defined + SPDesktop const *desktop = SP_ACTIVE_DESKTOP; Geom::Point const p_doc = desktop->dt2doc(p); @@ -441,10 +446,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, for (std::vector::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { bool const being_edited = (node_tool_active && (*it_p) == _paths_to_snap_to->back()); - //if true then this pathvector it_pv is currently being edited in the node tool - SnappedPoint s; - bool success = false; // char * svgd = sp_svg_write_path(**it_p); // std::cout << "Dumping the pathvector: " << svgd << std::endl; @@ -486,31 +488,12 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, NR::Coord const dist = Geom::distance(sp_doc, p_doc); if (dist < getSnapperTolerance()) { double t = MIN(*np, (*it_pv).size()); // make sure that t is within bounds; - //Geom::Curve const & curve = (*it_pv).at_index(int(t)); - if(is_straight_curve((*it_pv).at_index(int(t)))) { - // if we snap to a line segment, then return this line segment (leaves 1 DOF for snapping) - sc.lines.push_back(Inkscape::SnappedLineSegment(sp_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), start_pt, end_pt)); - } else { - // for curves other than line segments, we'll return just the closest snapped point - // (this is a fully constrained snap, no degrees of freedom left) - if (dist < s.getDistance()) { - // If this curve has multiple segments, then we will return only - // a single snapped point - s = SnappedPoint(sp_dt, SNAPTARGET_PATH, dist, getSnapperTolerance(), getSnapperAlwaysSnap()); - success = true; - } - } + Geom::Curve const *curve = &((*it_pv).at_index(int(t))); + sc.curves.push_back(Inkscape::SnappedCurve(from_2geom(sp_dt), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), curve)); } } } - } // End of: for (Geom::PathVector::iterator ....) - - // Return a snap point for each path in our collection. - // (unless we've already snapped to a line segment, see above) - if (success) { - sc.points.push_back(s); - } - + } // End of: for (Geom::PathVector::iterator ....) } } diff --git a/src/snap.cpp b/src/snap.cpp index 9c9a69a98..778294711 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -23,6 +23,7 @@ #include "sp-namedview.h" #include "snap.h" #include "snapped-line.h" +#include "snapped-curve.h" #include #include @@ -785,17 +786,17 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedCons sp_list.push_back(closestPoint); } - // search for the closest snapped line segment - Inkscape::SnappedLineSegment closestLineSegment; - if (getClosestSLS(sc.lines, closestLineSegment)) { - sp_list.push_back(Inkscape::SnappedPoint(closestLineSegment)); + // search for the closest snapped curve + Inkscape::SnappedCurve closestCurve; + if (getClosestCurve(sc.curves, closestCurve)) { + sp_list.push_back(Inkscape::SnappedPoint(closestCurve)); } - if (_intersectionLS) { - // search for the closest snapped intersection of line segments - Inkscape::SnappedPoint closestLineSegmentIntersection; - if (getClosestIntersectionSLS(sc.lines, closestLineSegmentIntersection)) { - sp_list.push_back(closestLineSegmentIntersection); + if (_intersectionCS) { + // search for the closest snapped intersection of curves + Inkscape::SnappedPoint closestCurvesIntersection; + if (getClosestIntersectionCS(sc.curves, p, closestCurvesIntersection)) { + sp_list.push_back(closestCurvesIntersection); } } diff --git a/src/snap.h b/src/snap.h index 981e91ecd..2ed786a63 100644 --- a/src/snap.h +++ b/src/snap.h @@ -127,9 +127,9 @@ public: bool getSnapModeGuide() const; void setSnapIntersectionGG(bool enabled) {_intersectionGG = enabled;} - void setSnapIntersectionLS(bool enabled) {_intersectionLS = enabled;} + void setSnapIntersectionCS(bool enabled) {_intersectionCS = enabled;} bool getSnapIntersectionGG() {return _intersectionGG;} - bool getSnapIntersectionLS() {return _intersectionLS;} + bool getSnapIntersectionCS() {return _intersectionCS;} void setIncludeItemCenter(bool enabled) { _include_item_center = enabled; @@ -167,7 +167,7 @@ private: bool _include_item_center; //If true, snapping nodes will also snap the item's center bool _intersectionGG; - bool _intersectionLS; + bool _intersectionCS; bool _snap_enabled_globally; //Toggles ALL snapping std::vector *_items_to_ignore; diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp new file mode 100644 index 000000000..c6b7edc8a --- /dev/null +++ b/src/snapped-curve.cpp @@ -0,0 +1,146 @@ +/** + * \file src/snapped-curve.cpp + * \brief SnappedCurve class. + * + * Authors: + * Diederik van Lierop + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include "snapped-curve.h" +#include "libnr/nr-values.h" +#include <2geom/crossing.h> +#include <2geom/path-intersection.h> +#include + +// These two are needed for SP_ACTIVE_DESKTOP; this is a dirty hack +#include "desktop.h" +#include "inkscape.h" + +Inkscape::SnappedCurve::SnappedCurve(NR::Point const &snapped_point, NR::Coord const &snapped_distance, NR::Coord const &snapped_tolerance, bool const &always_snap, Geom::Curve const *curve) +{ + _distance = snapped_distance; + _tolerance = snapped_tolerance; + _always_snap = always_snap; + _curve = curve; + _second_distance = NR_HUGE; + _second_tolerance = 0; + _second_always_snap = false; + _point = snapped_point; + _at_intersection = false; +} + +Inkscape::SnappedCurve::SnappedCurve() +{ + _distance = NR_HUGE; + _tolerance = 0; + _always_snap = false; + _curve = NULL; + _second_distance = NR_HUGE; + _second_tolerance = 0; + _second_always_snap = false; + _point = NR::Point(0,0); + _at_intersection = false; +} + +Inkscape::SnappedCurve::~SnappedCurve() +{ +} + +Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &curve, NR::Point const &p) const +{ + // Calculate the intersections of two curves, which are both within snapping range, and + // return only the closest intersection + // The point of intersection should be considered for snapping, but might be outside the snapping range + // PS: We need p (the location of the mouse pointer) for find out which intersection is the + // closest, as there might be multiple intersections of two curves + Geom::SimpleCrosser xr; + Geom::Crossings cs = xr.crossings(*(this->_curve), *(curve._curve)); + + if (cs.size() > 0) { + // There might be multiple intersections: find the closest + Geom::Coord best_dist = NR_HUGE; + Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE); + for (std::vector::const_iterator i = cs.begin(); i != cs.end(); i++) { + Geom::Point p_ix = this->_curve->pointAt((*i).ta); + Geom::Coord dist = Geom::distance(p_ix, p); + if (dist < best_dist) { + best_dist = dist; + best_p = p_ix; + } + } + + // Now we've found the closests intersection, return it as a SnappedPoint + bool const use_this_as_primary = _distance < curve.getDistance(); + Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve; + Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this; + // The intersection should in fact be returned in desktop coordinates, but for this + // we need a desktop: this is a dirty hack + SPDesktop const *desktop = SP_ACTIVE_DESKTOP; + best_p = desktop->dt2doc(best_p); + // TODO: Investigate whether it is possible to use document coordinates everywhere + // in the snapper code. Only the mouse position should be in desktop coordinates, I guess. + // All paths are already in document coords and we are certainly not going to change THAT. + return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, + secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap()); + } + + // No intersection + return SnappedPoint(NR::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false); +} + +// search for the closest snapped line +bool getClosestCurve(std::list const &list, Inkscape::SnappedCurve &result) +{ + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) { + result = *i; + success = true; + } + } + + return success; +} + +// search for the closest intersection of two snapped curves, which are both member of the same collection +bool getClosestIntersectionCS(std::list const &list, NR::Point const &p, Inkscape::SnappedPoint &result) +{ + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + std::list::const_iterator j = i; + j++; + for (; j != list.end(); j++) { + Inkscape::SnappedPoint sp = (*i).intersect(*j, p); + if (sp.getAtIntersection()) { + // if it's the first point + bool const c1 = !success; + // or, if it's closer + bool const c2 = sp.getDistance() < result.getDistance(); + // or, if it's just then look at the other distance + // (only relevant for snapped points which are at an intersection + bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); + // then prefer this point over the previous one + if (c1 || c2 || c3) { + result = sp; + success = true; + } + } + } + } + + return success; +} +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/snapped-curve.h b/src/snapped-curve.h new file mode 100644 index 000000000..28908c42f --- /dev/null +++ b/src/snapped-curve.h @@ -0,0 +1,55 @@ +#ifndef SEEN_SNAPPEDCURVE_H +#define SEEN_SNAPPEDCURVE_H + +/** + * \file src/snapped-curve.h + * \brief SnappedCurve class. + * + * Authors: + * Diederik van Lierop + * + * Released under GNU GPL, read the file 'COPYING' for more information. + */ + +#include +#include +#include "libnr/nr-coord.h" +#include "libnr/nr-point.h" +#include +#include "snapped-point.h" +#include <2geom/forward.h> + +namespace Inkscape +{ + +/// Class describing the result of an attempt to snap to a curve. +class SnappedCurve : public SnappedPoint +{ +public: + SnappedCurve(); + SnappedCurve(NR::Point const &snapped_point, NR::Coord const &snapped_distance, NR::Coord const &snapped_tolerance, bool const &always_snap, Geom::Curve const *curve); + ~SnappedCurve(); + Inkscape::SnappedPoint intersect(SnappedCurve const &curve, NR::Point const &p) const; //intersect with another SnappedCurve + +private: + Geom::Curve const *_curve; +}; + +} + +bool getClosestCurve(std::list const &list, Inkscape::SnappedCurve &result); +bool getClosestIntersectionCS(std::list const &list, NR::Point const &p, Inkscape::SnappedPoint &result); + + +#endif /* !SEEN_SNAPPEDCURVE_H */ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/src/snapper.h b/src/snapper.h index cf2732b24..a63ea14c5 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -20,12 +20,14 @@ #include "snapped-point.h" #include "snapped-line.h" +#include "snapped-curve.h" struct SnappedConstraints { std::list points; std::list lines; std::list grid_lines; std::list guide_lines; + std::list curves; }; struct SPNamedView; diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 7caa9407c..7783c334d 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -252,7 +252,7 @@ static void sp_namedview_build(SPObject *object, SPDocument *document, Inkscape: sp_object_read_attr(object, "inkscape:snap-guide"); sp_object_read_attr(object, "inkscape:snap-center"); sp_object_read_attr(object, "inkscape:snap-intersection-grid-guide"); - sp_object_read_attr(object, "inkscape:snap-intersection-line-segments"); + sp_object_read_attr(object, "inkscape:snap-intersection-paths"); sp_object_read_attr(object, "inkscape:object-paths"); sp_object_read_attr(object, "inkscape:object-nodes"); sp_object_read_attr(object, "inkscape:bbox-paths"); @@ -481,8 +481,8 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va nv->snap_manager.setSnapIntersectionGG(value ? sp_str_to_bool(value) : TRUE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; - case SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM: - nv->snap_manager.setSnapIntersectionLS(value ? sp_str_to_bool(value) : FALSE); + case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS: + nv->snap_manager.setSnapIntersectionCS(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_OBJECT_PATHS: diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 519f795db..a69e8858f 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -116,8 +116,8 @@ DocumentProperties::DocumentProperties() //Applies to both nodes and guides, but not to bboxes, that's why its located here _rcbic( _("Rotation _center"), _("Consider the rotation center of an object when snapping"), "inkscape:snap-center", _wr), _rcbsigg(_("_Grid with guides"), _("Snap to grid-guide intersections"), "inkscape:snap-intersection-grid-guide", _wr), - _rcbsils(_("_Line segments"), _("Snap to intersections of line segments ('snap to paths' must be enabled, see the previous tab)"), - "inkscape:snap-intersection-line-segments", _wr), + _rcbsils(_("_Paths"), _("Snap to intersections of paths ('snap to paths' must be enabled, see the previous tab)"), + "inkscape:snap-intersection-paths", _wr), //--------------------------------------------------------------- _grids_label_crea("", Gtk::ALIGN_LEFT), //TRANSLATORS: In Grid|_New translate only the word _New. It ref to grid @@ -495,7 +495,7 @@ DocumentProperties::update() _rcbsng.setActive (nv->snap_manager.getSnapModeGuide()); _rcbic.setActive (nv->snap_manager.getIncludeItemCenter()); _rcbsigg.setActive (nv->snap_manager.getSnapIntersectionGG()); - _rcbsils.setActive (nv->snap_manager.getSnapIntersectionLS()); + _rcbsils.setActive (nv->snap_manager.getSnapIntersectionCS()); _rcbsnop.setActive(nv->snap_manager.object.getSnapToItemPath()); _rcbsnon.setActive(nv->snap_manager.object.getSnapToItemNode()); _rcbsnbbp.setActive(nv->snap_manager.object.getSnapToBBoxPath()); -- 2.30.2