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(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point start_point_of_line, NR::Point 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 = snapped_tolerance;
21 _always_snap = always_snap;
22 _at_intersection = false;
23 _second_distance = NR_HUGE;
24 _second_tolerance = 0;
25 _second_always_snap = false;
26 }
28 Inkscape::SnappedLineSegment::SnappedLineSegment()
29 {
30 _start_point_of_line = NR::Point(0,0);
31 _end_point_of_line = NR::Point(0,0);
32 _point = NR::Point(0,0);
33 _distance = NR_HUGE;
34 _tolerance = 0;
35 _always_snap = false;
36 _at_intersection = false;
37 _second_distance = NR_HUGE;
38 _second_tolerance = 0;
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.to_2geom(), _end_point_of_line.to_2geom(),
51 line._start_point_of_line.to_2geom(), line._end_point_of_line.to_2geom(),
52 intersection_2geom);
53 NR::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.getDistance();
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 return SnappedPoint(intersection, primarySLS->getDistance(), primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true,
71 secondarySLS->getDistance(), secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap());
72 }
74 // No intersection
75 return SnappedPoint(intersection, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
76 };
80 Inkscape::SnappedLine::SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point normal_to_line, NR::Point point_on_line)
81 : _normal_to_line(normal_to_line), _point_on_line(point_on_line)
82 {
83 _distance = snapped_distance;
84 _tolerance = snapped_tolerance;
85 _always_snap = always_snap;
86 _second_distance = NR_HUGE;
87 _second_tolerance = 0;
88 _second_always_snap = false;
89 _point = snapped_point;
90 _at_intersection = false;
91 }
93 Inkscape::SnappedLine::SnappedLine()
94 {
95 _normal_to_line = NR::Point(0,0);
96 _point_on_line = NR::Point(0,0);
97 _distance = NR_HUGE;
98 _tolerance = 0;
99 _always_snap = false;
100 _second_distance = NR_HUGE;
101 _second_tolerance = 0;
102 _second_always_snap = false;
103 _point = NR::Point(0,0);
104 _at_intersection = false;
105 }
107 Inkscape::SnappedLine::~SnappedLine()
108 {
109 }
111 Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const
112 {
113 // Calculate the intersection of two lines, which are both within snapping range
114 // One could be a grid line, whereas the other could be a guide line
115 // The point of intersection should be considered for snapping, but might be outside the snapping range
117 Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
118 Geom::IntersectorKind result = Geom::line_intersection(getNormal().to_2geom(), getConstTerm(),
119 line.getNormal().to_2geom(), line.getConstTerm(), intersection_2geom);
120 NR::Point intersection(intersection_2geom);
122 if (result == Geom::intersects) {
123 /* If a snapper has been told to "always snap", then this one should be preferred
124 * over the other, if that other one has not been told so. (The preferred snapper
125 * will be labelled "primary" below)
126 */
127 bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap();
128 /* If neither or both have been told to "always snap", then cast a vote based on
129 * the snapped distance. For this we should consider the distance to the snapped
130 * line, not the distance to the intersection.
131 *
132 * The relevant snapped distance is the distance to the closest snapped line, not the
133 * distance to the intersection. For example, when a box is almost aligned with a grid
134 * in both horizontal and vertical directions, the distance to the intersection of the
135 * grid lines will always be larger then the distance to a grid line. We will be snapping
136 * to the closest snapped point however, so if we ever want to snap to the intersection
137 * then the distance to it should at least be equal to the other distance, not greater
138 * than it, as that would rule the intersection out when comparing it with regular snappoint,
139 * as the latter will always be closer
140 */
141 bool const c2 = _distance < line.getDistance();
142 bool const use_this_as_primary = c1 || c2;
143 Inkscape::SnappedLine const *primarySL = use_this_as_primary ? this : &line;
144 Inkscape::SnappedLine const *secondarySL = use_this_as_primary ? &line : this;
145 return SnappedPoint(intersection, primarySL->getDistance(), primarySL->getTolerance(), primarySL->getAlwaysSnap(), true,
146 secondarySL->getDistance(), secondarySL->getTolerance(), secondarySL->getAlwaysSnap());
147 }
149 // No intersection
150 return SnappedPoint(intersection, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
151 }
153 // search for the closest snapped line segment
154 bool getClosestSLS(std::list<Inkscape::SnappedLineSegment> &list, Inkscape::SnappedLineSegment &result)
155 {
156 bool success = false;
158 for (std::list<Inkscape::SnappedLineSegment>::const_iterator i = list.begin(); i != list.end(); i++) {
159 if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {
160 result = *i;
161 success = true;
162 }
163 }
165 return success;
166 }
168 // search for the closest intersection of two snapped line segments, which are both member of the same collection
169 bool getClosestIntersectionSLS(std::list<Inkscape::SnappedLineSegment> &list, Inkscape::SnappedPoint &result)
170 {
171 bool success = false;
173 for (std::list<Inkscape::SnappedLineSegment>::const_iterator i = list.begin(); i != list.end(); i++) {
174 std::list<Inkscape::SnappedLineSegment>::const_iterator j = i;
175 j++;
176 for (; j != list.end(); j++) {
177 Inkscape::SnappedPoint sp = (*i).intersect(*j);
178 if (sp.getAtIntersection()) {
179 // if it's the first point
180 bool const c1 = !success;
181 // or, if it's closer
182 bool const c2 = sp.getDistance() < result.getDistance();
183 // or, if it's just then look at the other distance
184 // (only relevant for snapped points which are at an intersection
185 bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance());
186 // then prefer this point over the previous one
187 if (c1 || c2 || c3) {
188 result = sp;
189 success = true;
190 }
191 }
192 }
193 }
195 return success;
196 }
198 // search for the closest snapped line
199 bool getClosestSL(std::list<Inkscape::SnappedLine> &list, Inkscape::SnappedLine &result)
200 {
201 bool success = false;
203 for (std::list<Inkscape::SnappedLine>::const_iterator i = list.begin(); i != list.end(); i++) {
204 if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) {
205 result = *i;
206 success = true;
207 }
208 }
210 return success;
211 }
213 // search for the closest intersection of two snapped lines, which are both member of the same collection
214 bool getClosestIntersectionSL(std::list<Inkscape::SnappedLine> &list, Inkscape::SnappedPoint &result)
215 {
216 bool success = false;
218 for (std::list<Inkscape::SnappedLine>::const_iterator i = list.begin(); i != list.end(); i++) {
219 std::list<Inkscape::SnappedLine>::const_iterator j = i;
220 j++;
221 for (; j != list.end(); j++) {
222 Inkscape::SnappedPoint sp = (*i).intersect(*j);
223 if (sp.getAtIntersection()) {
224 // if it's the first point
225 bool const c1 = !success;
226 // or, if it's closer
227 bool const c2 = sp.getDistance() < result.getDistance();
228 // or, if it's just then look at the other distance
229 // (only relevant for snapped points which are at an intersection
230 bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance());
231 // then prefer this point over the previous one
232 if (c1 || c2 || c3) {
233 result = sp;
234 success = true;
235 }
236 }
237 }
238 }
240 return success;
241 }
243 // search for the closest intersection of two snapped lines, which are in two different collections
244 bool getClosestIntersectionSL(std::list<Inkscape::SnappedLine> &list1, std::list<Inkscape::SnappedLine> &list2, Inkscape::SnappedPoint &result)
245 {
246 bool success = false;
248 for (std::list<Inkscape::SnappedLine>::const_iterator i = list1.begin(); i != list1.end(); i++) {
249 for (std::list<Inkscape::SnappedLine>::const_iterator j = list2.begin(); j != list2.end(); j++) {
250 Inkscape::SnappedPoint sp = (*i).intersect(*j);
251 if (sp.getAtIntersection()) {
252 // if it's the first point
253 bool const c1 = !success;
254 // or, if it's closer
255 bool const c2 = sp.getDistance() < result.getDistance();
256 // or, if it's just then look at the other distance
257 // (only relevant for snapped points which are at an intersection
258 bool const c3 = (sp.getDistance() == result.getDistance()) && (sp.getSecondDistance() < result.getSecondDistance());
259 // then prefer this point over the previous one
260 if (c1 || c2 || c3) {
261 result = sp;
262 success = true;
263 }
264 }
265 }
266 }
268 return success;
269 }
271 /*
272 Local Variables:
273 mode:c++
274 c-file-style:"stroustrup"
275 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
276 indent-tabs-mode:nil
277 fill-column:99
278 End:
279 */
280 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :