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::Crossings cs = crossings(*(this->_curve), *(curve._curve));\r
59 \r
60 if (cs.size() > 0) {\r
61 // There might be multiple intersections: find the closest\r
62 Geom::Coord best_dist = NR_HUGE;\r
63 Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE);\r
64 for (std::vector<Geom::Crossing>::const_iterator i = cs.begin(); i != cs.end(); i++) {\r
65 Geom::Point p_ix = this->_curve->pointAt((*i).ta);\r
66 Geom::Coord dist = Geom::distance(p_ix, p);\r
67 if (dist < best_dist) {\r
68 best_dist = dist;\r
69 best_p = p_ix;\r
70 }\r
71 }\r
72 \r
73 // Now we've found the closests intersection, return it as a SnappedPoint\r
74 bool const use_this_as_primary = _distance < curve.getDistance();\r
75 Inkscape::SnappedCurve const *primaryC = use_this_as_primary ? this : &curve;\r
76 Inkscape::SnappedCurve const *secondaryC = use_this_as_primary ? &curve : this;\r
77 // The intersection should in fact be returned in desktop coordinates, but for this\r
78 // we need a desktop: this is a dirty hack\r
79 SPDesktop const *desktop = SP_ACTIVE_DESKTOP;\r
80 best_p = desktop->dt2doc(best_p);\r
81 // TODO: Investigate whether it is possible to use document coordinates everywhere\r
82 // in the snapper code. Only the mouse position should be in desktop coordinates, I guess.\r
83 // All paths are already in document coords and we are certainly not going to change THAT.\r
84 return SnappedPoint(from_2geom(best_p), Inkscape::SNAPTARGET_PATH_INTERSECTION, primaryC->getDistance(), primaryC->getTolerance(), primaryC->getAlwaysSnap(), true, \r
85 secondaryC->getDistance(), secondaryC->getTolerance(), secondaryC->getAlwaysSnap());\r
86 }\r
87 \r
88 // No intersection\r
89 return SnappedPoint(Geom::Point(NR_HUGE, NR_HUGE), SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, NR_HUGE, 0, false);\r
90 }\r
91 \r
92 // search for the closest snapped line\r
93 bool getClosestCurve(std::list<Inkscape::SnappedCurve> const &list, Inkscape::SnappedCurve &result) \r
94 {\r
95 bool success = false;\r
96 \r
97 for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
98 if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {\r
99 result = *i;\r
100 success = true;\r
101 } \r
102 }\r
103 \r
104 return success; \r
105 }\r
106 \r
107 // search for the closest intersection of two snapped curves, which are both member of the same collection\r
108 bool getClosestIntersectionCS(std::list<Inkscape::SnappedCurve> const &list, Geom::Point const &p, Inkscape::SnappedPoint &result)\r
109 {\r
110 bool success = false;\r
111 \r
112 for (std::list<Inkscape::SnappedCurve>::const_iterator i = list.begin(); i != list.end(); i++) {\r
113 std::list<Inkscape::SnappedCurve>::const_iterator j = i;\r
114 j++;\r
115 for (; j != list.end(); j++) {\r
116 Inkscape::SnappedPoint sp = (*i).intersect(*j, p);\r
117 if (sp.getAtIntersection()) {\r
118 // if it's the first point\r
119 bool const c1 = !success;\r
120 // or, if it's closer \r
121 bool const c2 = sp.getDistance() < result.getDistance();\r
122 // or, if it's just then look at the other distance \r
123 // (only relevant for snapped points which are at an intersection\r
124 bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance()); \r
125 // then prefer this point over the previous one\r
126 if (c1 || c2 || c3) { \r
127 result = sp;\r
128 success = true;\r
129 }\r
130 } \r
131 }\r
132 }\r
133 \r
134 return success; \r
135 }\r
136 /*\r
137 Local Variables:\r
138 mode:c++\r
139 c-file-style:"stroustrup"\r
140 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
141 indent-tabs-mode:nil\r
142 fill-column:99\r
143 End:\r
144 */\r
145 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :\r