Code

- Fix bug #304405 (snapping to an intersection of lines at infinity is wrong)
[inkscape.git] / src / snapped-line.cpp
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;
109 Inkscape::SnappedLine::~SnappedLine()
113 Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const
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);
150 // search for the closest snapped line segment
151 bool getClosestSLS(std::list<Inkscape::SnappedLineSegment> const &list, Inkscape::SnappedLineSegment &result)
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;
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)
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;
195 // search for the closest snapped line
196 bool getClosestSL(std::list<Inkscape::SnappedLine> const &list, Inkscape::SnappedLine &result)
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;
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)
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;
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)
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;
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 :