Code

Clean up knutux's fix for the snapper crash.
[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* 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 }
53 /* FIXME: lots of cut-and-paste here.  This needs some
54 ** functor voodoo to cut it all down a bit.
55 */
57 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
58                                              NR::Point const &p,
59                                              SPItem const *it) const
61 {
62     std::list<SPItem const *> lit;
63     lit.push_back(it);
64     return freeSnap(t, p, lit);
65 }
68 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
69                                              NR::Point const &p,
70                                              std::list<SPItem const *> const &it) const
71 {
72     Inkscape::SnappedPoint r(p, NR_HUGE);
74     SnapperList const snappers = getSnappers();
75     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
76         Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
77         if (s.getDistance() < r.getDistance()) {
78             r = s;
79         }
80     }
82     return r;
83 }
86 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
87                                                     NR::Point const &p,
88                                                     NR::Point const &c,
89                                                     SPItem const *it) const
90 {
91     std::list<SPItem const *> lit;
92     lit.push_back(it);
93     return constrainedSnap(t, p, c, lit);
94 }
97 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
98                                                     NR::Point const &p,
99                                                     NR::Point const &c,
100                                                     std::list<SPItem const *> const &it) const
102     Inkscape::SnappedPoint r(p, NR_HUGE);
104     SnapperList const snappers = getSnappers();
105     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
106         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
107         if (s.getDistance() < r.getDistance()) {
108             r = s;
109         }
110     }
112     return r;
116 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
117                                                             std::vector<NR::Point> const &p,
118                                                             std::list<SPItem const *> const &it,
119                                                             NR::Point const &tr) const
121     if (willSnapSomething() == false) {
122         return std::make_pair(tr, false);
123     }
125     NR::Point best_translation = tr;
126     NR::Coord best_distance = NR_HUGE;
128     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
129         /* Translated version of this point */
130         NR::Point const q = *i + tr;
131         /* Snap it */
132         Inkscape::SnappedPoint s = freeSnap(t, q, it);
133         if (s.getDistance() < NR_HUGE) {
134             /* Resulting translation */
135             NR::Point const r = s.getPoint() - *i;
136             NR::Coord const d = NR::L2(r);
137             if (d < best_distance) {
138                 best_distance = d;
139                 best_translation = r;
140             }
141         }
142     }
144     return std::make_pair(best_translation, best_distance < NR_HUGE);
149 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
150                                                                    std::vector<NR::Point> const &p,
151                                                                    NR::Point const &c,
152                                                                    std::list<SPItem const *> const &it,
153                                                                    NR::Point const &tr) const
155     if (willSnapSomething() == false) {
156         return std::make_pair(tr, false);
157     }
159     NR::Point best_translation = tr;
160     NR::Coord best_distance = NR_HUGE;
162     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
163         /* Translated version of this point */
164         NR::Point const q = *i + tr;
165         /* Snap it */
166         Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it);
167         if (s.getDistance() < NR_HUGE) {
168             /* Resulting translation */
169             NR::Point const r = s.getPoint() - *i;
170             NR::Coord const d = NR::L2(r);
171             if (d < best_distance) {
172                 best_distance = d;
173                 best_translation = r;
174             }
175         }
176     }
178     return std::make_pair(best_translation, best_distance < NR_HUGE);
186 /// Minimal distance to norm before point is considered for snap.
187 static const double MIN_DIST_NORM = 1.0;
189 /**
190  * Try to snap \a req in one dimension.
191  *
192  * \param nv NamedView to use.
193  * \param req Point to snap; updated to the snapped point if a snap occurred.
194  * \param dim Dimension to snap in.
195  * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE
196  *    if no snap occurred.
197  */
198 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
199                              NR::Dim2 const dim, SPItem const *it)
201     return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
204 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
205                              NR::Dim2 const dim, std::list<SPItem const *> const &it)
207     return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
211 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
212                                 NR::Point &req, NR::Point const &d,
213                                 SPItem const *it)
215     std::list<SPItem const *> lit;
216     lit.push_back(it);
217     return namedview_vector_snap(nv, t, req, d, lit);
220 /**
221  * Look for snap point along the line described by the point \a req
222  * and the direction vector \a d.
223  * Modifies req to the snap point, if one is found.
224  * \return The distance from \a req to the snap point along the vector \a d,
225  * or \c NR_HUGE if no snap point was found.
226  *
227  * \pre d \81â\89\81  (0, 0).
228  */
229 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
230                                 NR::Point &req, NR::Point const &d,
231                                 std::list<SPItem const *> const &it)
233     g_assert(nv != NULL);
234     g_assert(SP_IS_NAMEDVIEW(nv));
236     SnapManager::SnapperList const snappers = nv->snap_manager.getSnappers();
238     NR::Coord best = NR_HUGE;
239     for (SnapManager::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
240         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it);
241         if (s.getDistance() < best) {
242             req = s.getPoint();
243             best = s.getDistance();
244         }
245     }
247     return best;
251 /*
252  * functions for lists of points
253  *
254  * All functions take a list of NR::Point and parameter indicating the proposed transformation.
255  * They return the updated transformation parameter.
256  */
258 /**
259  * Snap list of points in two dimensions.
260  */
261 std::pair<double, bool> namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
262                                                    const std::vector<NR::Point> &p, NR::Point const &norm,
263                                                    NR::scale const &s, std::list<SPItem const *> const &it)
265     using NR::X;
266     using NR::Y;
268     SnapManager const &m = nv->snap_manager;
270     if (m.willSnapSomething() == false) {
271         return std::make_pair(s[X], false);
272     }
274     NR::Coord dist = NR_HUGE;
275     double ratio = fabs(s[X]);
276     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
277         NR::Point const &q = *i;
278         NR::Point check = ( q - norm ) * s + norm;
279         if (NR::LInfty( q - norm ) > MIN_DIST_NORM) {
280             NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it);
281             if (d < dist) {
282                 dist = d;
283                 NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] )  >
284                                               fabs( q[Y] - norm[Y] ) )
285                                             ? X
286                                             : Y );
287                 ratio = ( ( check[dominant] - norm[dominant] )
288                           / ( q[dominant] - norm[dominant] ) );
289             }
290         }
291     }
293     return std::make_pair(ratio, dist < NR_HUGE);
297 /**
298  * Try to snap points in \a p after they have been scaled by \a sx with respect to
299  * the origin \a norm.  The best snap is the one that changes the scale least.
300  *
301  * \return Pair containing snapped scale and a flag which is true if a snap was made.
302  */
303 std::pair<double, bool> namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t,
304                                                       const std::vector<NR::Point> &p, NR::Point const &norm,
305                                                       double const sx, NR::Dim2 dim,
306                                                       std::list<const SPItem *> const &it)
308     SnapManager const &m = nv->snap_manager;
309     if (m.willSnapSomething() == false) {
310         return std::make_pair(sx, false);
311     }
313     g_assert(dim < 2);
315     NR::Coord dist = NR_HUGE;
316     double scale = sx;
318     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
319         NR::Point q = *i;
320         NR::Point check = q;
322         /* Scaled version of the point we are looking at */
323         check[dim] = (sx * (q - norm) + norm)[dim];
325         if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) {
326             /* Snap this point */
327             const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it);
328             /* Work out the resulting scale factor */
329             double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]);
331             if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) {
332                 /* This is either the first point, or the snapped scale
333                 ** is the closest yet to the original.
334                 */
335                 scale = snapped_scale;
336                 dist = d;
337             }
338         }
339     }
341     return std::make_pair(scale, dist < NR_HUGE);
344 /**
345  * Try to snap points after they have been skewed.
346  */
347 double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t,
348                                     const std::vector<NR::Point> &p, NR::Point const &norm,
349                                     double const sx, NR::Dim2 const dim)
351     SnapManager const &m = nv->snap_manager;
353     if (m.willSnapSomething() == false) {
354         return sx;
355     }
357     g_assert(dim < 2);
359     gdouble dist = NR_HUGE;
360     gdouble skew = sx;
362     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
363         NR::Point q = *i;
364         NR::Point check = q;
365         // apply shear
366         check[dim] += sx * (q[!dim] - norm[!dim]);
367         if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) {
368             const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL);
369             if (d < fabs (dist)) {
370                 dist = d;
371                 skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]);
372             }
373         }
374     }
376     return skew;
380 /*
381   Local Variables:
382   mode:c++
383   c-file-style:"stroustrup"
384   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
385   indent-tabs-mode:nil
386   fill-column:99
387   End:
388 */
389 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :