Code

3d4a48005d5deb0229326ca60d1d22515cff5692
[inkscape.git] / src / snap.cpp
1 #define __SP_DESKTOP_SNAP_C__
3 /**
4  * \file snap.cpp
5  *
6  * \brief Various snapping methods
7  *
8  * Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   Frank Felfe <innerspace@iname.com>
11  *   Carl Hetherington <inkscape@carlh.net>
12  *
13  * Copyright (C) 1999-2002 Authors
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #include "sp-namedview.h"
19 #include "snap.h"
20 #include <libnr/nr-point-fns.h>
21 #include <libnr/nr-scale-ops.h>
22 #include <libnr/nr-values.h>
24 SnapManager::SnapManager(SPNamedView const *v) : grid(v, 0), guide(v, 0), object(v, 0)
25 {
27 }
29 SnapManager::SnapperList SnapManager::getSnappers() const
30 {
31     SnapManager::SnapperList s;
32     s.push_back(&grid);
33     s.push_back(&guide);
34     s.push_back(&object);
35     return s;
36 }
38 /**
39  * \return true if one of the snappers will try to snap something.
40  */
41 bool SnapManager::willSnapSomething() const
42 {
43     SnapperList const s = getSnappers();
44     SnapperList::const_iterator i = s.begin();
45     while (i != s.end() && (*i)->willSnapSomething() == false) {
46         i++;
47     }
49     return (i != s.end());
50 }
52 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
53                                              NR::Point const &p,
54                                              SPItem const *it) const
56 {
57     std::list<SPItem const *> lit;
58     lit.push_back(it);
59     return freeSnap(t, p, lit);
60 }
63 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
64                                              NR::Point const &p,
65                                              std::list<SPItem const *> const &it) const
66 {
67     Inkscape::SnappedPoint r(p, NR_HUGE);
69     SnapperList const snappers = getSnappers();
70     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
71         Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
72         if (s.getDistance() < r.getDistance()) {
73             r = s;
74         }
75     }
77     return r;
78 }
81 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
82                                                     NR::Point const &p,
83                                                     Inkscape::Snapper::ConstraintLine const &c,
84                                                     SPItem const *it) const
85 {
86     std::list<SPItem const *> lit;
87     lit.push_back(it);
88     return constrainedSnap(t, p, c, lit);
89 }
92 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
93                                                     NR::Point const &p,
94                                                     Inkscape::Snapper::ConstraintLine const &c,
95                                                     std::list<SPItem const *> const &it) const
96 {
97     Inkscape::SnappedPoint r(p, NR_HUGE);
99     SnapperList const snappers = getSnappers();
100     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
101         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
102         if (s.getDistance() < r.getDistance()) {
103             r = s;
104         }
105     }
107     return r;
111 std::pair<NR::Point, bool> SnapManager::_snapTransformed(
112     Inkscape::Snapper::PointType type,
113     std::vector<NR::Point> const &points,
114     std::list<SPItem const *> const &ignore,
115     bool constrained,
116     Inkscape::Snapper::ConstraintLine const &constraint,
117     Transformation transformation_type,
118     NR::Point const &transformation,
119     NR::Point const &origin,
120     NR::Dim2 dim,
121     bool uniform) const
123     /* We have a list of points, which we are proposing to transform in some way.  We need to see
124     ** if any of these points, when transformed, snap to anything.  If they do, we return the
125     ** appropriate transformation with `true'; otherwise we return the original scale with `false'.
126     */
128     /* Quick check to see if we have any snappers that are enabled */
129     if (willSnapSomething() == false) {
130         return std::make_pair(transformation, false);
131     }
133     /* The current best transformation */
134     NR::Point best_transformation = transformation;
135     
136     /* The current best metric for the best transformation; lower is better, NR_HUGE
137     ** means that we haven't snapped anything.
138     */
139     double best_metric = NR_HUGE;
141     for (std::vector<NR::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
143         /* Work out the transformed version of this point */
144         NR::Point transformed;
145         switch (transformation_type) {
146             case TRANSLATION:
147                 transformed = *i + transformation;
148                 break;
149             case SCALE:
150                 transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin;
151                 break;
152             case STRETCH:
153             {
154                 NR::scale s(1, 1);
155                 if (uniform)
156                     s[NR::X] = s[NR::Y] = transformation[dim];
157                 else {
158                     s[dim] = transformation[dim];
159                     s[1 - dim] = 1;
160                 }
161                 transformed = ((*i - origin) * s) + origin;
162                 break;
163             }
164             case SKEW:
165                 transformed = *i;
166                 transformed[dim] += transformation[dim] * ((*i)[1 - dim] - origin[1 - dim]);
167                 break;
168             default:
169                 g_assert_not_reached();
170         }
171         
172         /* Snap it */
173         Inkscape::SnappedPoint const snapped = constrained ?
174             constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore);
176         if (snapped.getDistance() < NR_HUGE) {
177             /* We snapped.  Find the transformation that describes where the snapped point has
178             ** ended up, and also the metric for this transformation.
179             */
180             NR::Point result;
181             NR::Coord metric;
182             switch (transformation_type) {
183                 case TRANSLATION:
184                     result = snapped.getPoint() - *i;
185                     metric = NR::L2(result);
186                     break;
187                 case SCALE:
188                 {
189                     NR::Point const a = (snapped.getPoint() - origin);
190                     NR::Point const b = (*i - origin);
191                     result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
192                     metric = std::abs(NR::L2(result) - NR::L2(transformation));
193                     break;
194                 }
195                 case STRETCH:
196                 {
197                     for (int j = 0; j < 2; j++) {
198                         if (uniform || j == dim) {
199                             result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
200                         } else {
201                             result[j] = 1;
202                         }
203                     }
204                     metric = std::abs(result[dim] - transformation[dim]);
205                     break;
206                 }
207                 case SKEW:
208                     result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
209                     metric = std::abs(result[dim] - transformation[dim]);
210                     break;
211                 default:
212                     g_assert_not_reached();
213             }
215             /* Note it if it's the best so far */
216             if (metric < best_metric && metric != 0) {
217                 best_transformation = result;
218                 best_metric = metric;
219             }
220         }
221     }
222         
223     return std::make_pair(best_transformation, best_metric < NR_HUGE);
227 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
228                                                             std::vector<NR::Point> const &p,
229                                                             std::list<SPItem const *> const &it,
230                                                             NR::Point const &tr) const
232     return _snapTransformed(
233         t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(), NR::X, false
234         );
239 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
240                                                                    std::vector<NR::Point> const &p,
241                                                                    std::list<SPItem const *> const &it,
242                                                                    Inkscape::Snapper::ConstraintLine const &c,
243                                                                    NR::Point const &tr) const
245     return _snapTransformed(
246         t, p, it, true, c, TRANSLATION, tr, NR::Point(), NR::X, false
247         );
250 std::pair<NR::scale, bool> SnapManager::freeSnapScale(Inkscape::Snapper::PointType t,
251                                                       std::vector<NR::Point> const &p,
252                                                       std::list<SPItem const *> const &it,
253                                                       NR::scale const &s,
254                                                       NR::Point const &o) const
256     return _snapTransformed(
257         t, p, it, false, NR::Point(), SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
258         );
262 std::pair<NR::scale, bool> SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t,
263                                                              std::vector<NR::Point> const &p,
264                                                              std::list<SPItem const *> const &it,
265                                                              Inkscape::Snapper::ConstraintLine const &c,
266                                                              NR::scale const &s,
267                                                              NR::Point const &o) const
269     return _snapTransformed(
270         t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
271         );
275 std::pair<NR::Coord, bool> SnapManager::freeSnapStretch(Inkscape::Snapper::PointType t,
276                                                         std::vector<NR::Point> const &p,
277                                                         std::list<SPItem const *> const &it,
278                                                         NR::Coord const &s,
279                                                         NR::Point const &o,
280                                                         NR::Dim2 d,
281                                                         bool u) const
283    std::pair<NR::Point, bool> const r = _snapTransformed(
284         t, p, it, false, NR::Point(), STRETCH, NR::Point(s, s), o, d, u
285         );
287    return std::make_pair(r.first[d], r.second);
291 std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointType t,
292                                                      std::vector<NR::Point> const &p,
293                                                      std::list<SPItem const *> const &it,
294                                                      NR::Coord const &s,
295                                                      NR::Point const &o,
296                                                      NR::Dim2 d) const
298    std::pair<NR::Point, bool> const r = _snapTransformed(
299         t, p, it, false, NR::Point(), SKEW, NR::Point(s, s), o, d, false
300         );
302    return std::make_pair(r.first[d], r.second);
305 /*
306   Local Variables:
307   mode:c++
308   c-file-style:"stroustrup"
309   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
310   indent-tabs-mode:nil
311   fill-column:99
312   End:
313 */
314 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :