Code

940d4c77208c6f17f898423baf692080a67892d0
[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::SnapperList SnapManager::getSnappers() const
25 {
26     SnapManager::SnapperList s;
27     s.push_back(&grid);
28     s.push_back(&guide);
29     s.push_back(&object);
30     return s;
31 }
33 /**
34  * \return true if one of the snappers will try to snap something.
35  */
36 bool SnapManager::willSnapSomething() const
37 {
38     SnapperList const s = getSnappers();
39     SnapperList::const_iterator i = s.begin();
40     while (i != s.end() && (*i)->willSnapSomething() == false) {
41         i++;
42     }
44     return (i != s.end());
45 }
48 /* FIXME: lots of cut-and-paste here.  This needs some
49 ** functor voodoo to cut it all down a bit.
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                                                     NR::Point 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                                                     NR::Point 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::freeSnapTranslation(Inkscape::Snapper::PointType t,
112                                                             std::vector<NR::Point> const &p,
113                                                             std::list<SPItem const *> const &it,
114                                                             NR::Point const &tr) const
116     if (willSnapSomething() == false) {
117         return std::make_pair(tr, false);
118     }
120     NR::Point best_translation = tr;
121     NR::Coord best_distance = NR_HUGE;
123     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
124         /* Translated version of this point */
125         NR::Point const q = *i + tr;
126         /* Snap it */
127         Inkscape::SnappedPoint s = freeSnap(t, q, it);
128         if (s.getDistance() < NR_HUGE) {
129             /* Resulting translation */
130             NR::Point const r = s.getPoint() - *i;
131             NR::Coord const d = NR::L2(r);
132             if (d < best_distance) {
133                 best_distance = d;
134                 best_translation = r;
135             }
136         }
137     }
139     return std::make_pair(best_translation, best_distance < NR_HUGE);
144 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
145                                                                    std::vector<NR::Point> const &p,
146                                                                    NR::Point const &c,
147                                                                    std::list<SPItem const *> const &it,
148                                                                    NR::Point const &tr) const
150     if (willSnapSomething() == false) {
151         return std::make_pair(tr, false);
152     }
154     NR::Point best_translation = tr;
155     NR::Coord best_distance = NR_HUGE;
157     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
158         /* Translated version of this point */
159         NR::Point const q = *i + tr;
160         /* Snap it */
161         Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it);
162         if (s.getDistance() < NR_HUGE) {
163             /* Resulting translation */
164             NR::Point const r = s.getPoint() - *i;
165             NR::Coord const d = NR::L2(r);
166             if (d < best_distance) {
167                 best_distance = d;
168                 best_translation = r;
169             }
170         }
171     }
173     return std::make_pair(best_translation, best_distance < NR_HUGE);
181 /// Minimal distance to norm before point is considered for snap.
182 static const double MIN_DIST_NORM = 1.0;
184 /**
185  * Try to snap \a req in one dimension.
186  *
187  * \param nv NamedView to use.
188  * \param req Point to snap; updated to the snapped point if a snap occurred.
189  * \param dim Dimension to snap in.
190  * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE
191  *    if no snap occurred.
192  */
193 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
194                              NR::Dim2 const dim, SPItem const *it)
196     return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
199 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
200                              NR::Dim2 const dim, std::list<SPItem const *> const &it)
202     return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
206 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
207                                 NR::Point &req, NR::Point const &d,
208                                 SPItem const *it)
210     std::list<SPItem const *> lit;
211     lit.push_back(it);
212     return namedview_vector_snap(nv, t, req, d, lit);
215 /**
216  * Look for snap point along the line described by the point \a req
217  * and the direction vector \a d.
218  * Modifies req to the snap point, if one is found.
219  * \return The distance from \a req to the snap point along the vector \a d,
220  * or \c NR_HUGE if no snap point was found.
221  *
222  * \pre d \81â\89\81  (0, 0).
223  */
224 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
225                                 NR::Point &req, NR::Point const &d,
226                                 std::list<SPItem const *> const &it)
228     g_assert(nv != NULL);
229     g_assert(SP_IS_NAMEDVIEW(nv));
231     SnapManager::SnapperList const snappers = nv->snap_manager.getSnappers();
233     NR::Coord best = NR_HUGE;
234     for (SnapManager::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
235         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it);
236         if (s.getDistance() < best) {
237             req = s.getPoint();
238             best = s.getDistance();
239         }
240     }
242     return best;
246 /*
247  * functions for lists of points
248  *
249  * All functions take a list of NR::Point and parameter indicating the proposed transformation.
250  * They return the updated transformation parameter.
251  */
253 /**
254  * Snap list of points in two dimensions.
255  */
256 std::pair<double, bool> namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
257                                                    const std::vector<NR::Point> &p, NR::Point const &norm,
258                                                    NR::scale const &s, std::list<SPItem const *> const &it)
260     using NR::X;
261     using NR::Y;
263     SnapManager const &m = nv->snap_manager;
265     if (m.willSnapSomething() == false) {
266         return std::make_pair(s[X], false);
267     }
269     NR::Coord dist = NR_HUGE;
270     double ratio = fabs(s[X]);
271     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
272         NR::Point const &q = *i;
273         NR::Point check = ( q - norm ) * s + norm;
274         if (NR::LInfty( q - norm ) > MIN_DIST_NORM) {
275             NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it);
276             if (d < dist) {
277                 dist = d;
278                 NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] )  >
279                                               fabs( q[Y] - norm[Y] ) )
280                                             ? X
281                                             : Y );
282                 ratio = ( ( check[dominant] - norm[dominant] )
283                           / ( q[dominant] - norm[dominant] ) );
284             }
285         }
286     }
288     return std::make_pair(ratio, dist < NR_HUGE);
292 /**
293  * Try to snap points in \a p after they have been scaled by \a sx with respect to
294  * the origin \a norm.  The best snap is the one that changes the scale least.
295  *
296  * \return Pair containing snapped scale and a flag which is true if a snap was made.
297  */
298 std::pair<double, bool> namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t,
299                                                       const std::vector<NR::Point> &p, NR::Point const &norm,
300                                                       double const sx, NR::Dim2 dim,
301                                                       std::list<const SPItem *> const &it)
303     SnapManager const &m = nv->snap_manager;
304     if (m.willSnapSomething() == false) {
305         return std::make_pair(sx, false);
306     }
308     g_assert(dim < 2);
310     NR::Coord dist = NR_HUGE;
311     double scale = sx;
313     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
314         NR::Point q = *i;
315         NR::Point check = q;
317         /* Scaled version of the point we are looking at */
318         check[dim] = (sx * (q - norm) + norm)[dim];
320         if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) {
321             /* Snap this point */
322             const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it);
323             /* Work out the resulting scale factor */
324             double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]);
326             if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) {
327                 /* This is either the first point, or the snapped scale
328                 ** is the closest yet to the original.
329                 */
330                 scale = snapped_scale;
331                 dist = d;
332             }
333         }
334     }
336     return std::make_pair(scale, dist < NR_HUGE);
339 /**
340  * Try to snap points after they have been skewed.
341  */
342 double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t,
343                                     const std::vector<NR::Point> &p, NR::Point const &norm,
344                                     double const sx, NR::Dim2 const dim)
346     SnapManager const &m = nv->snap_manager;
348     if (m.willSnapSomething() == false) {
349         return sx;
350     }
352     g_assert(dim < 2);
354     gdouble dist = NR_HUGE;
355     gdouble skew = sx;
357     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
358         NR::Point q = *i;
359         NR::Point check = q;
360         // apply shear
361         check[dim] += sx * (q[!dim] - norm[!dim]);
362         if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) {
363             const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL);
364             if (d < fabs (dist)) {
365                 dist = d;
366                 skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]);
367             }
368         }
369     }
371     return skew;
375 /*
376   Local Variables:
377   mode:c++
378   c-file-style:"stroustrup"
379   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
380   indent-tabs-mode:nil
381   fill-column:99
382   End:
383 */
384 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :