Code

Snap to intersections of any kind of path (were we previously only could snap to...
[inkscape.git] / src / snapped-curve.cpp
1 /**\r
2  *    \file src/snapped-curve.cpp\r
3  *    \brief SnappedCurve class.\r
4  *\r
5  *    Authors:\r
6  *      Diederik van Lierop <mail@diedenrezi.nl>\r
7  *\r
8  *    Released under GNU GPL, read the file 'COPYING' for more information.\r
9  */\r
10 \r
11 #include "snapped-curve.h"\r
12 #include "libnr/nr-values.h"\r
13 #include <2geom/crossing.h>\r
14 #include <2geom/path-intersection.h>\r
15 #include <libnr/nr-convert2geom.h>\r
16 \r
17 // These two are needed for SP_ACTIVE_DESKTOP; this is a dirty hack\r
18 #include "desktop.h"\r
19 #include "inkscape.h"\r
20 \r
21 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
22 {\r
23         _distance = snapped_distance;\r
24     _tolerance = snapped_tolerance;\r
25     _always_snap = always_snap;\r
26     _curve = curve;\r
27         _second_distance = NR_HUGE;\r
28     _second_tolerance = 0;\r
29     _second_always_snap = false;\r
30         _point = snapped_point;\r
31         _at_intersection = false;\r
32 }\r
33 \r
34 Inkscape::SnappedCurve::SnappedCurve() \r
35 {\r
36         _distance = NR_HUGE;\r
37     _tolerance = 0;\r
38     _always_snap = false;\r
39     _curve = NULL;\r
40         _second_distance = NR_HUGE;\r
41     _second_tolerance = 0;\r
42     _second_always_snap = false;\r
43         _point = NR::Point(0,0);\r
44         _at_intersection = false;\r
45 }\r
46 \r
47 Inkscape::SnappedCurve::~SnappedCurve()\r
48 {\r
49 }\r
50 \r
51 Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &curve, NR::Point const &p) const \r
52 {\r
53     // Calculate the intersections of two curves, which are both within snapping range, and\r
54     // return only the closest intersection\r
55     // The point of intersection should be considered for snapping, but might be outside the snapping range\r
56     // PS: We need p (the location of the mouse pointer) for find out which intersection is the\r
57     // closest, as there might be multiple intersections of two curves\r
58     Geom::SimpleCrosser xr;\r
59     Geom::Crossings cs = xr.crossings(*(this->_curve), *(curve._curve));\r
60      \r
61     if (cs.size() > 0) {\r
62         // There might be multiple intersections: find the closest\r
63         Geom::Coord best_dist = NR_HUGE;\r
64         Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE);\r
65         for (std::vector<Geom::Crossing>::const_iterator i = cs.begin(); i != cs.end(); i++) {\r
66             Geom::Point p_ix = this->_curve->pointAt((*i).ta);\r
67             Geom::Coord dist = Geom::distance(p_ix, p);\r
68             if (dist < best_dist) {\r
69                 best_dist = dist;\r
70                 best_p = p_ix;\r
71             }\r
72         }\r
73         \r
74         // Now we've found the closests intersection, return it as a SnappedPoint\r
75         bool const use_this_as_primary = _distance < curve.getDistance();\r
76         Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;\r
77         Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;\r
78         // The intersection should in fact be returned in desktop coordinates, but for this\r
79         // we need a desktop: this is a dirty hack\r
80         SPDesktop const *desktop = SP_ACTIVE_DESKTOP;\r
81         best_p = desktop->dt2doc(best_p);\r
82         // TODO: Investigate whether it is possible to use document coordinates everywhere\r
83         // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.\r
84         // All paths are already in document coords and we are certainly not going to change THAT.\r
85         return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, \r
86                                           secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap());\r
87     }\r
88     \r
89     // No intersection\r
90     return SnappedPoint(NR::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);\r
91 }\r
92 \r
93 // search for the closest snapped line\r
94 bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) \r
95 {\r
96     bool success = false;\r
97     \r
98     for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
99         if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {\r
100             result = *i;\r
101             success = true;\r
102         }   \r
103     }\r
104     \r
105     return success; \r
106 }\r
107 \r
108 // search for the closest intersection of two snapped curves, which are both member of the same collection\r
109 bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, NR::Point const &p, Inkscape::SnappedPoint &result)\r
110 {\r
111     bool success = false;\r
112     \r
113     for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
114         std::list<Inkscape::SnappedCurve>::const_iterator j = i;\r
115         j++;\r
116         for (; j != list.end(); j++) {\r
117             Inkscape::SnappedPoint sp = (*i).intersect(*j, p);\r
118             if (sp.getAtIntersection()) {\r
119                 // if it's the first point\r
120                 bool const c1 = !success;\r
121                 // or, if it's closer             \r
122                 bool const c2 = sp.getDistance() < result.getDistance();\r
123                 // or, if it's just then look at the other distance \r
124                 // (only relevant for snapped points which are at an intersection\r
125                 bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); \r
126                 // then prefer this point over the previous one\r
127                 if (c1 || c2 || c3) {  \r
128                     result = sp;\r
129                     success = true;\r
130                 }\r
131             }               \r
132         }\r
133     }\r
134     \r
135     return success; \r
136 }\r
137 /*\r
138   Local Variables:\r
139   mode:c++\r
140   c-file-style:"stroustrup"\r
141   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
142   indent-tabs-mode:nil\r
143   fill-column:99\r
144   End:\r
145 */\r
146 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r