1 /**
2 * \file src/snapped-line.cpp
3 * \brief SnappedLine class.
4 *
5 * Authors:
6 * Diederik van Lierop <mail@diedenrezi.nl>
7 *
8 * Released under GNU GPL, read the file 'COPYING' for more information.
9 */
11 #include "snapped-line.h"
12 #include <2geom/geom.h>
13 #include "libnr/nr-values.h"
15 Inkscape::SnappedLineSegment::SnappedLineSegment(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &start_point_of_line, Geom::Point const &end_point_of_line)
16 : _start_point_of_line(start_point_of_line), _end_point_of_line(end_point_of_line)
17 {
18 _point = snapped_point;
19 _distance = snapped_distance;
20 _tolerance = std::max(snapped_tolerance, 1.0);
21 _always_snap = always_snap;
22 _at_intersection = false;
23 _second_distance = NR_HUGE;
24 _second_tolerance = 1;
25 _second_always_snap = false;
26 }
28 Inkscape::SnappedLineSegment::SnappedLineSegment()
29 {
30 _start_point_of_line = Geom::Point(0,0);
31 _end_point_of_line = Geom::Point(0,0);
32 _point = Geom::Point(0,0);
33 _distance = NR_HUGE;
34 _tolerance = 1;
35 _always_snap = false;
36 _at_intersection = false;
37 _second_distance = NR_HUGE;
38 _second_tolerance = 1;
39 _second_always_snap = false;
40 }
43 Inkscape::SnappedLineSegment::~SnappedLineSegment()
44 {
45 }
47 Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegment const &line) const
48 {
49 Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
50 Geom::IntersectorKind result = segment_intersect(_start_point_of_line, _end_point_of_line,
51 line._start_point_of_line, line._end_point_of_line,
52 intersection_2geom);
53 Geom::Point intersection(intersection_2geom);
55 if (result == Geom::intersects) {
56 /* If a snapper has been told to "always snap", then this one should be preferred
57 * over the other, if that other one has not been told so. (The preferred snapper
58 * will be labelled "primary" below)
59 */
60 bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap(); //do not use _tolerance directly!
61 /* If neither or both have been told to "always snap", then cast a vote based on
62 * the snapped distance. For this we should consider the distance to the snapped
63 * line, not the distance to the intersection.
64 * See the comment in Inkscape::SnappedLine::intersect
65 */
66 bool const c2 = _distance < line.getSnapDistance();
67 bool const use_this_as_primary = c1 || c2;
68 Inkscape::SnappedLineSegment const *primarySLS = use_this_as_primary ? this : &line;
69 Inkscape::SnappedLineSegment const *secondarySLS = use_this_as_primary ? &line : this;
70 Geom::Coord primaryDist = use_this_as_primary ? Geom::L2(intersection_2geom - this->getPoint()) : Geom::L2(intersection_2geom - line.getPoint());
71 Geom::Coord secondaryDist = use_this_as_primary ? Geom::L2(intersection_2geom - line.getPoint()) : Geom::L2(intersection_2geom - this->getPoint());
72 return SnappedPoint(intersection, SNAPTARGET_PATH_INTERSECTION, primaryDist, primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, true,
73 secondaryDist, secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap());
74 }
76 // No intersection
77 return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
78 };
82 Inkscape::SnappedLine::SnappedLine(Geom::Point const &snapped_point, Geom::Coord const &snapped_distance, Geom::Coord const &snapped_tolerance, bool const &always_snap, Geom::Point const &normal_to_line, Geom::Point const &point_on_line)
83 : _normal_to_line(normal_to_line), _point_on_line(point_on_line)
84 {
85 _distance = snapped_distance;
86 _tolerance = std::max(snapped_tolerance, 1.0);
87 _always_snap = always_snap;
88 _second_distance = NR_HUGE;
89 _second_tolerance = 1;
90 _second_always_snap = false;
91 _point = snapped_point;
92 _at_intersection = false;
93 }
95 Inkscape::SnappedLine::SnappedLine()
96 {
97 _normal_to_line = Geom::Point(0,0);
98 _point_on_line = Geom::Point(0,0);
99 _distance = NR_HUGE;
100 _tolerance = 1;
101 _always_snap = false;
102 _second_distance = NR_HUGE;
103 _second_tolerance = 1;
104 _second_always_snap = false;
105 _point = Geom::Point(0,0);
106 _at_intersection = false;
107 }
109 Inkscape::SnappedLine::~SnappedLine()
110 {
111 }
113 Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const
114 {
115 // Calculate the intersection of two lines, which are both within snapping range
116 // One could be a grid line, whereas the other could be a guide line
117 // The point of intersection should be considered for snapping, but might be outside the snapping range
119 Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
120 Geom::IntersectorKind result = Geom::line_intersection(getNormal(), getConstTerm(),
121 line.getNormal(), line.getConstTerm(), intersection_2geom);
122 Geom::Point intersection(intersection_2geom);
124 if (result == Geom::intersects) {
125 /* If a snapper has been told to "always snap", then this one should be preferred
126 * over the other, if that other one has not been told so. (The preferred snapper
127 * will be labelled "primary" below)
128 */
129 bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap();
130 /* If neither or both have been told to "always snap", then cast a vote based on
131 * the snapped distance. For this we should consider the distance to the snapped
132 * line or to the intersection
133 */
134 bool const c2 = _distance < line.getSnapDistance();
135 bool const use_this_as_primary = c1 || c2;
136 Inkscape::SnappedLine const *primarySL = use_this_as_primary ? this : &line;
137 Inkscape::SnappedLine const *secondarySL = use_this_as_primary ? &line : this;
138 Geom::Coord primaryDist = use_this_as_primary ? Geom::L2(intersection_2geom - this->getPoint()) : Geom::L2(intersection_2geom - line.getPoint());
139 Geom::Coord secondaryDist = use_this_as_primary ? Geom::L2(intersection_2geom - line.getPoint()) : Geom::L2(intersection_2geom - this->getPoint());
140 return SnappedPoint(intersection, Inkscape::SNAPTARGET_UNDEFINED, primaryDist, primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, true,
141 secondaryDist, secondarySL->getTolerance(), secondarySL->getAlwaysSnap());
142 // The type of the snap target is yet undefined, as we cannot tell whether
143 // we're snapping to grid or the guide lines; must be set by on a higher level
144 }
146 // No intersection
147 return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
148 }
150 // search for the closest snapped line segment
151 bool getClosestSLS(std::list<Inkscape::SnappedLineSegment> const &list, Inkscape::SnappedLineSegment &result)
152 {
153 bool success = false;
155 for (std::list<Inkscape::SnappedLineSegment>::const_iterator i = list.begin(); i != list.end(); i++) {
156 if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) {
157 result = *i;
158 success = true;
159 }
160 }
162 return success;
163 }
165 // search for the closest intersection of two snapped line segments, which are both member of the same collection
166 bool getClosestIntersectionSLS(std::list<Inkscape::SnappedLineSegment> const &list, Inkscape::SnappedPoint &result)
167 {
168 bool success = false;
170 for (std::list<Inkscape::SnappedLineSegment>::const_iterator i = list.begin(); i != list.end(); i++) {
171 std::list<Inkscape::SnappedLineSegment>::const_iterator j = i;
172 j++;
173 for (; j != list.end(); j++) {
174 Inkscape::SnappedPoint sp = (*i).intersect(*j);
175 if (sp.getAtIntersection()) {
176 // if it's the first point
177 bool const c1 = !success;
178 // or, if it's closer
179 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
180 // or, if it's just then look at the other distance
181 // (only relevant for snapped points which are at an intersection
182 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance());
183 // then prefer this point over the previous one
184 if (c1 || c2 || c3) {
185 result = sp;
186 success = true;
187 }
188 }
189 }
190 }
192 return success;
193 }
195 // search for the closest snapped line
196 bool getClosestSL(std::list<Inkscape::SnappedLine> const &list, Inkscape::SnappedLine &result)
197 {
198 bool success = false;
200 for (std::list<Inkscape::SnappedLine>::const_iterator i = list.begin(); i != list.end(); i++) {
201 if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) {
202 result = *i;
203 success = true;
204 }
205 }
207 return success;
208 }
210 // search for the closest intersection of two snapped lines, which are both member of the same collection
211 bool getClosestIntersectionSL(std::list<Inkscape::SnappedLine> const &list, Inkscape::SnappedPoint &result)
212 {
213 bool success = false;
215 for (std::list<Inkscape::SnappedLine>::const_iterator i = list.begin(); i != list.end(); i++) {
216 std::list<Inkscape::SnappedLine>::const_iterator j = i;
217 j++;
218 for (; j != list.end(); j++) {
219 Inkscape::SnappedPoint sp = (*i).intersect(*j);
220 if (sp.getAtIntersection()) {
221 // if it's the first point
222 bool const c1 = !success;
223 // or, if it's closer
224 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
225 // or, if it's just then look at the other distance
226 // (only relevant for snapped points which are at an intersection
227 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance());
228 // then prefer this point over the previous one
229 if (c1 || c2 || c3) {
230 result = sp;
231 success = true;
232 }
233 }
234 }
235 }
237 return success;
238 }
240 // search for the closest intersection of two snapped lines, which are in two different collections
241 bool getClosestIntersectionSL(std::list<Inkscape::SnappedLine> const &list1, std::list<Inkscape::SnappedLine> const &list2, Inkscape::SnappedPoint &result)
242 {
243 bool success = false;
245 for (std::list<Inkscape::SnappedLine>::const_iterator i = list1.begin(); i != list1.end(); i++) {
246 for (std::list<Inkscape::SnappedLine>::const_iterator j = list2.begin(); j != list2.end(); j++) {
247 Inkscape::SnappedPoint sp = (*i).intersect(*j);
248 if (sp.getAtIntersection()) {
249 // if it's the first point
250 bool const c1 = !success;
251 // or, if it's closer
252 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
253 // or, if it's just then look at the other distance
254 // (only relevant for snapped points which are at an intersection
255 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance());
256 // then prefer this point over the previous one
257 if (c1 || c2 || c3) {
258 result = sp;
259 success = true;
260 }
261 }
262 }
263 }
265 return success;
266 }
268 /*
269 Local Variables:
270 mode:c++
271 c-file-style:"stroustrup"
272 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
273 indent-tabs-mode:nil
274 fill-column:99
275 End:
276 */
277 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :