Code

Snap to intersections of any kind of path (were we previously only could snap to...
authordvlierop2 <dvlierop2@users.sourceforge.net>
Fri, 8 Aug 2008 22:28:49 +0000 (22:28 +0000)
committerdvlierop2 <dvlierop2@users.sourceforge.net>
Fri, 8 Aug 2008 22:28:49 +0000 (22:28 +0000)
src/attributes-test.h
src/attributes.cpp
src/attributes.h
src/object-snapper.cpp
src/snap.cpp
src/snap.h
src/snapped-curve.cpp [new file with mode: 0644]
src/snapped-curve.h [new file with mode: 0644]
src/snapper.h
src/sp-namedview.cpp
src/ui/dialog/document-properties.cpp

index c8287f76935945f8c4a7b0f52fbeae00fe252b9d..25167e75be6912b9617ffc3b91066f6f25337a53 100644 (file)
@@ -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},
index c671446923c03fcd9bee320c99d390645a10b2ab..e64cedf36a6d5cdf0be1766439ce0b4a54466281 100644 (file)
@@ -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"},
index c1c4dcadc7ff8f2179b59f30755ad7913148aa11..82fba18a93ec2405c21b91fbd6975a1f32d9097a 100644 (file)
@@ -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,
index 3bebc7b22586a789cdc45c60caa44cc1bd47d5b7..a3f7383db64808ffe63bacaae052480efef0fd38 100644 (file)
@@ -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<Geom::PathVector*>::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 ....)        
     }    
 }
 
index 9c9a69a989ea08058978475c449f5dfeb7279689..7782947111374fd0d129730ee4551e8bcf7c2d25 100644 (file)
@@ -23,6 +23,7 @@
 #include "sp-namedview.h"
 #include "snap.h"
 #include "snapped-line.h"
+#include "snapped-curve.h"
 
 #include <libnr/nr-point-fns.h>
 #include <libnr/nr-scale-ops.h>
@@ -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);
            }
     }    
 
index 981e91ecdfaba1e7fcf665622753633d058c5145..2ed786a633f5dfb38eab02ccd902cbfecab0c5c3 100644 (file)
@@ -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<SPItem const *> *_items_to_ignore;
diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp
new file mode 100644 (file)
index 0000000..c6b7edc
--- /dev/null
@@ -0,0 +1,146 @@
+/**\r
+ *    \file src/snapped-curve.cpp\r
+ *    \brief SnappedCurve class.\r
+ *\r
+ *    Authors:\r
+ *      Diederik van Lierop <mail@diedenrezi.nl>\r
+ *\r
+ *    Released under GNU GPL, read the file 'COPYING' for more information.\r
+ */\r
+\r
+#include "snapped-curve.h"\r
+#include "libnr/nr-values.h"\r
+#include <2geom/crossing.h>\r
+#include <2geom/path-intersection.h>\r
+#include <libnr/nr-convert2geom.h>\r
+\r
+// These two are needed for SP_ACTIVE_DESKTOP; this is a dirty hack\r
+#include "desktop.h"\r
+#include "inkscape.h"\r
+\r
+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)\r
+{\r
+       _distance = snapped_distance;\r
+    _tolerance = snapped_tolerance;\r
+    _always_snap = always_snap;\r
+    _curve = curve;\r
+       _second_distance = NR_HUGE;\r
+    _second_tolerance = 0;\r
+    _second_always_snap = false;\r
+       _point = snapped_point;\r
+       _at_intersection = false;\r
+}\r
+\r
+Inkscape::SnappedCurve::SnappedCurve() \r
+{\r
+       _distance = NR_HUGE;\r
+    _tolerance = 0;\r
+    _always_snap = false;\r
+    _curve = NULL;\r
+       _second_distance = NR_HUGE;\r
+    _second_tolerance = 0;\r
+    _second_always_snap = false;\r
+       _point = NR::Point(0,0);\r
+       _at_intersection = false;\r
+}\r
+\r
+Inkscape::SnappedCurve::~SnappedCurve()\r
+{\r
+}\r
+\r
+Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &curve, NR::Point const &p) const \r
+{\r
+    // Calculate the intersections of two curves, which are both within snapping range, and\r
+    // return only the closest intersection\r
+    // The point of intersection should be considered for snapping, but might be outside the snapping range\r
+    // PS: We need p (the location of the mouse pointer) for find out which intersection is the\r
+    // closest, as there might be multiple intersections of two curves\r
+    Geom::SimpleCrosser xr;\r
+    Geom::Crossings cs = xr.crossings(*(this->_curve), *(curve._curve));\r
+     \r
+    if (cs.size() > 0) {\r
+        // There might be multiple intersections: find the closest\r
+        Geom::Coord best_dist = NR_HUGE;\r
+        Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE);\r
+        for (std::vector<Geom::Crossing>::const_iterator i = cs.begin(); i != cs.end(); i++) {\r
+            Geom::Point p_ix = this->_curve->pointAt((*i).ta);\r
+            Geom::Coord dist = Geom::distance(p_ix, p);\r
+            if (dist < best_dist) {\r
+                best_dist = dist;\r
+                best_p = p_ix;\r
+            }\r
+        }\r
+        \r
+        // Now we've found the closests intersection, return it as a SnappedPoint\r
+        bool const use_this_as_primary = _distance < curve.getDistance();\r
+        Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;\r
+        Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;\r
+        // The intersection should in fact be returned in desktop coordinates, but for this\r
+        // we need a desktop: this is a dirty hack\r
+        SPDesktop const *desktop = SP_ACTIVE_DESKTOP;\r
+        best_p = desktop->dt2doc(best_p);\r
+        // TODO: Investigate whether it is possible to use document coordinates everywhere\r
+        // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.\r
+        // All paths are already in document coords and we are certainly not going to change THAT.\r
+        return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, \r
+                                          secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap());\r
+    }\r
+    \r
+    // No intersection\r
+    return SnappedPoint(NR::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);\r
+}\r
+\r
+// search for the closest snapped line\r
+bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) \r
+{\r
+    bool success = false;\r
+    \r
+    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
+        if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {\r
+            result = *i;\r
+            success = true;\r
+        }   \r
+    }\r
+    \r
+    return success; \r
+}\r
+\r
+// search for the closest intersection of two snapped curves, which are both member of the same collection\r
+bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, NR::Point const &p, Inkscape::SnappedPoint &result)\r
+{\r
+    bool success = false;\r
+    \r
+    for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
+        std::list<Inkscape::SnappedCurve>::const_iterator j = i;\r
+        j++;\r
+        for (; j != list.end(); j++) {\r
+            Inkscape::SnappedPoint sp = (*i).intersect(*j, p);\r
+            if (sp.getAtIntersection()) {\r
+                // if it's the first point\r
+                bool const c1 = !success;\r
+                // or, if it's closer             \r
+                bool const c2 = sp.getDistance() < result.getDistance();\r
+                // or, if it's just then look at the other distance \r
+                // (only relevant for snapped points which are at an intersection\r
+                bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); \r
+                // then prefer this point over the previous one\r
+                if (c1 || c2 || c3) {  \r
+                    result = sp;\r
+                    success = true;\r
+                }\r
+            }               \r
+        }\r
+    }\r
+    \r
+    return success; \r
+}\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
diff --git a/src/snapped-curve.h b/src/snapped-curve.h
new file mode 100644 (file)
index 0000000..28908c4
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef SEEN_SNAPPEDCURVE_H\r
+#define SEEN_SNAPPEDCURVE_H\r
+\r
+/**\r
+ *    \file src/snapped-curve.h\r
+ *    \brief SnappedCurve class.\r
+ *\r
+ *    Authors:\r
+ *      Diederik van Lierop <mail@diedenrezi.nl>\r
+ *\r
+ *    Released under GNU GPL, read the file 'COPYING' for more information.\r
+ */\r
+\r
+#include <vector>\r
+#include <list>\r
+#include "libnr/nr-coord.h"\r
+#include "libnr/nr-point.h"\r
+#include <libnr/nr-point-fns.h>\r
+#include "snapped-point.h"\r
+#include <2geom/forward.h>\r
+\r
+namespace Inkscape\r
+{\r
+\r
+/// Class describing the result of an attempt to snap to a curve.\r
+class SnappedCurve : public SnappedPoint\r
+{\r
+public:\r
+    SnappedCurve();\r
+    SnappedCurve(NR::Point const &snapped_point, NR::Coord const &snapped_distance, NR::Coord const &snapped_tolerance, bool const &always_snap, Geom::Curve const *curve);\r
+    ~SnappedCurve();\r
+    Inkscape::SnappedPoint intersect(SnappedCurve const &curve, NR::Point const &p) const; //intersect with another SnappedCurve\r
+    \r
+private:\r
+    Geom::Curve const *_curve;\r
+};\r
+\r
+}\r
+\r
+bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result);\r
+bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, NR::Point const &p, Inkscape::SnappedPoint &result);\r
+\r
+\r
+#endif /* !SEEN_SNAPPEDCURVE_H */\r
+\r
+/*\r
+  Local Variables:\r
+  mode:c++\r
+  c-file-style:"stroustrup"\r
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
+  indent-tabs-mode:nil\r
+  fill-column:99\r
+  End:\r
+*/\r
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r
index cf2732b2426c2f58c8246c35c58e5b4f61f937d7..a63ea14c5950dafacb6ffbf4b59af1c51e233b27 100644 (file)
 
 #include "snapped-point.h"
 #include "snapped-line.h"
+#include "snapped-curve.h"
 
 struct SnappedConstraints {
     std::list<Inkscape::SnappedPoint> points;
     std::list<Inkscape::SnappedLineSegment> lines;
     std::list<Inkscape::SnappedLine> grid_lines;
     std::list<Inkscape::SnappedLine> guide_lines;
+    std::list<Inkscape::SnappedCurve> curves;
 };
 
 struct SPNamedView;
index 7caa9407cfef05de719503749dd831bae9ebb9eb..7783c334d04e87814657f5fe85c6a74afb40a6c1 100644 (file)
@@ -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:
index 519f795db1527929601125d5eaded7ff16fb5e2f..a69e8858f8c0b70fca8b1845f2b6e3096ffc111e 100644 (file)
@@ -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());