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(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::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 = Geom::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, Geom::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(Geom::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, Geom::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