Code

1) Improve the way the distance to the pointer is taken into account when finding...
[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);
54         
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         return SnappedPoint(intersection, SNAPTARGET_PATH_INTERSECTION, primarySLS->getSnapDistance(), primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true, true,
71                                           secondarySLS->getSnapDistance(), secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap());
72         }
73     
74     // No intersection
75     return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
76 };
80 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)
81     : _normal_to_line(normal_to_line), _point_on_line(point_on_line)
82 {
83         _distance = snapped_distance;
84     _tolerance = std::max(snapped_tolerance, 1.0);
85     _always_snap = always_snap;
86         _second_distance = NR_HUGE;
87     _second_tolerance = 1;
88     _second_always_snap = false;
89         _point = snapped_point;
90         _at_intersection = false;
91 }
93 Inkscape::SnappedLine::SnappedLine() 
94 {
95         _normal_to_line = Geom::Point(0,0);
96         _point_on_line = Geom::Point(0,0);
97         _distance = NR_HUGE;
98     _tolerance = 1;
99     _always_snap = false;
100         _second_distance = NR_HUGE;
101     _second_tolerance = 1;
102     _second_always_snap = false;
103         _point = Geom::Point(0,0);
104         _at_intersection = false;
107 Inkscape::SnappedLine::~SnappedLine()
111 Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const 
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
116         
117         Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
118         Geom::IntersectorKind result = Geom::line_intersection(getNormal(), getConstTerm(), 
119                                    line.getNormal(), line.getConstTerm(), intersection_2geom);
120         Geom::Point intersection(intersection_2geom);
121          
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.getSnapDistance();
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, Inkscape::SNAPTARGET_UNDEFINED, primarySL->getSnapDistance(), primarySL->getTolerance(), primarySL->getAlwaysSnap(), true, true,
146                                           secondarySL->getSnapDistance(), secondarySL->getTolerance(), secondarySL->getAlwaysSnap());
147         // The type of the snap target is yet undefined, as we cannot tell whether 
148         // we're snapping to grid or the guide lines; must be set by on a higher level                                          
149     }
150     
151     // No intersection
152     return SnappedPoint(intersection, SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false, NR_HUGE, 0, false);
155 // search for the closest snapped line segment
156 bool getClosestSLS(std::list<Inkscape::SnappedLineSegment> const &list, Inkscape::SnappedLineSegment &result) 
158         bool success = false;
159         
160         for (std::list<Inkscape::SnappedLineSegment>::const_iterator i = list.begin(); i != list.end(); i++) {
161                 if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) {
162                         result = *i;
163                         success = true;
164                 }       
165         }
166         
167         return success; 
170 // search for the closest intersection of two snapped line segments, which are both member of the same collection
171 bool getClosestIntersectionSLS(std::list<Inkscape::SnappedLineSegment> const &list, Inkscape::SnappedPoint &result)
173         bool success = false;
174         
175         for (std::list<Inkscape::SnappedLineSegment>::const_iterator i = list.begin(); i != list.end(); i++) {
176                 std::list<Inkscape::SnappedLineSegment>::const_iterator j = i;
177                 j++;
178                 for (; j != list.end(); j++) {
179                         Inkscape::SnappedPoint sp = (*i).intersect(*j);
180                         if (sp.getAtIntersection()) {
181                                 // if it's the first point
182                 bool const c1 = !success;
183                                 // or, if it's closer             
184                                 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
185                                 // or, if it's just then look at the other distance 
186                                 // (only relevant for snapped points which are at an intersection
187                                 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); 
188                                 // then prefer this point over the previous one
189                                 if (c1 || c2 || c3) {  
190                                         result = sp;
191                                         success = true;
192                                 }
193                         }
194                 }
195         }
196         
197         return success; 
200 // search for the closest snapped line
201 bool getClosestSL(std::list<Inkscape::SnappedLine> const &list, Inkscape::SnappedLine &result) 
203         bool success = false;
204         
205         for (std::list<Inkscape::SnappedLine>::const_iterator i = list.begin(); i != list.end(); i++) {
206                 if ((i == list.begin()) || (*i).getSnapDistance() < result.getSnapDistance()) {
207                         result = *i;
208                         success = true;
209                 }       
210         }
211         
212         return success; 
215 // search for the closest intersection of two snapped lines, which are both member of the same collection
216 bool getClosestIntersectionSL(std::list<Inkscape::SnappedLine> const &list, Inkscape::SnappedPoint &result)
218         bool success = false;
219         
220         for (std::list<Inkscape::SnappedLine>::const_iterator i = list.begin(); i != list.end(); i++) {
221                 std::list<Inkscape::SnappedLine>::const_iterator j = i;
222                 j++;
223                 for (; j != list.end(); j++) {
224                         Inkscape::SnappedPoint sp = (*i).intersect(*j);
225                         if (sp.getAtIntersection()) {
226                                 // if it's the first point
227                 bool const c1 = !success;
228                                 // or, if it's closer             
229                                 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
230                                 // or, if it's just then look at the other distance 
231                                 // (only relevant for snapped points which are at an intersection
232                                 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); 
233                                 // then prefer this point over the previous one
234                                 if (c1 || c2 || c3) {  
235                                         result = sp;
236                                         success = true;
237                                 }
238                         }                               
239                 }
240         }
241         
242         return success; 
245 // search for the closest intersection of two snapped lines, which are in two different collections
246 bool getClosestIntersectionSL(std::list<Inkscape::SnappedLine> const &list1, std::list<Inkscape::SnappedLine> const &list2, Inkscape::SnappedPoint &result)
248         bool success = false;
249         
250         for (std::list<Inkscape::SnappedLine>::const_iterator i = list1.begin(); i != list1.end(); i++) {
251                 for (std::list<Inkscape::SnappedLine>::const_iterator j = list2.begin(); j != list2.end(); j++) {
252                         Inkscape::SnappedPoint sp = (*i).intersect(*j);
253                         if (sp.getAtIntersection()) {
254                                 // if it's the first point
255                 bool const c1 = !success;
256                                 // or, if it's closer             
257                                 bool const c2 = sp.getSnapDistance() < result.getSnapDistance();
258                                 // or, if it's just then look at the other distance 
259                                 // (only relevant for snapped points which are at an intersection
260                                 bool const c3 = (sp.getSnapDistance() == result.getSnapDistance()) && (sp.getSecondSnapDistance() < result.getSecondSnapDistance()); 
261                                 // then prefer this point over the previous one
262                                 if (c1 || c2 || c3) {  
263                                         result = sp;
264                                         success = true;
265                                 }
266                         }                               
267                 }
268         }
269         
270         return success;
273 /*
274   Local Variables:
275   mode:c++
276   c-file-style:"stroustrup"
277   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
278   indent-tabs-mode:nil
279   fill-column:99
280   End:
281 */
282 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :