Code

r11451@tres: ted | 2006-04-17 22:21:33 -0700
[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>
25 /**
26  * \return true if one of the snappers will try to snap something.
27  */
28 bool SnapManager::willSnapSomething() const
29 {
30     SPNamedView::SnapperList s = namedview->getSnappers();
31     SPNamedView::SnapperList::const_iterator i = s.begin();
32     while (i != s.end() && (*i)->willSnapSomething() == false) {
33         i++;
34     }
36     return (i != s.end());
37 }
40 /* FIXME: lots of cut-and-paste here.  This needs some
41 ** functor voodoo to cut it all down a bit.
42 */
44 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
45                                              NR::Point const &p,
46                                              SPItem const *it) const
48 {
49     std::list<SPItem const *> lit;
50     lit.push_back(it);
51     return freeSnap(t, p, lit);
52 }
55 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
56                                              NR::Point const &p,
57                                              std::list<SPItem const *> const &it) const
58 {
59     Inkscape::SnappedPoint r(p, NR_HUGE);
61     SPNamedView::SnapperList snappers = namedview->getSnappers();
62     for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
63         Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
64         if (s.getDistance() < r.getDistance()) {
65             r = s;
66         }
67     }
69     return r;
70 }
73 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
74                                                     NR::Point const &p,
75                                                     NR::Point const &c,
76                                                     SPItem const *it) const
77 {
78     std::list<SPItem const *> lit;
79     lit.push_back(it);
80     return constrainedSnap(t, p, c, lit);
81 }
84 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
85                                                     NR::Point const &p,
86                                                     NR::Point const &c,
87                                                     std::list<SPItem const *> const &it) const
88 {
89     Inkscape::SnappedPoint r(p, NR_HUGE);
91     SPNamedView::SnapperList snappers = namedview->getSnappers();
92     for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
93         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
94         if (s.getDistance() < r.getDistance()) {
95             r = s;
96         }
97     }
99     return r;
103 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
104                                                             std::vector<NR::Point> const &p,
105                                                             std::list<SPItem const *> const &it,
106                                                             NR::Point const &tr) const
108     if (willSnapSomething() == false) {
109         return std::make_pair(tr, false);
110     }
112     NR::Point best_translation = tr;
113     NR::Coord best_distance = NR_HUGE;
115     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
116         /* Translated version of this point */
117         NR::Point const q = *i + tr;
118         /* Snap it */
119         Inkscape::SnappedPoint s = freeSnap(t, q, it);
120         if (s.getDistance() < NR_HUGE) {
121             /* Resulting translation */
122             NR::Point const r = s.getPoint() - *i;
123             NR::Coord const d = NR::L2(r);
124             if (d < best_distance) {
125                 best_distance = d;
126                 best_translation = r;
127             }
128         }
129     }
131     return std::make_pair(best_translation, best_distance < NR_HUGE);
136 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
137                                                                    std::vector<NR::Point> const &p,
138                                                                    NR::Point const &c,
139                                                                    std::list<SPItem const *> const &it,
140                                                                    NR::Point const &tr) const
142     if (willSnapSomething() == false) {
143         return std::make_pair(tr, false);
144     }
146     NR::Point best_translation = tr;
147     NR::Coord best_distance = NR_HUGE;
149     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
150         /* Translated version of this point */
151         NR::Point const q = *i + tr;
152         /* Snap it */
153         Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it);
154         if (s.getDistance() < NR_HUGE) {
155             /* Resulting translation */
156             NR::Point const r = s.getPoint() - *i;
157             NR::Coord const d = NR::L2(r);
158             if (d < best_distance) {
159                 best_distance = d;
160                 best_translation = r;
161             }
162         }
163     }
165     return std::make_pair(best_translation, best_distance < NR_HUGE);
173 /// Minimal distance to norm before point is considered for snap.
174 static const double MIN_DIST_NORM = 1.0;
176 /**
177  * Try to snap \a req in one dimension.
178  *
179  * \param nv NamedView to use.
180  * \param req Point to snap; updated to the snapped point if a snap occurred.
181  * \param dim Dimension to snap in.
182  * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE
183  *    if no snap occurred.
184  */
185 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
186                              NR::Dim2 const dim, SPItem const *it)
188     return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
191 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
192                              NR::Dim2 const dim, std::list<SPItem const *> const &it)
194     return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
198 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
199                                 NR::Point &req, NR::Point const &d,
200                                 SPItem const *it)
202     std::list<SPItem const *> lit;
203     lit.push_back(it);
204     return namedview_vector_snap(nv, t, req, d, lit);
207 /**
208  * Look for snap point along the line described by the point \a req
209  * and the direction vector \a d.
210  * Modifies req to the snap point, if one is found.
211  * \return The distance from \a req to the snap point along the vector \a d,
212  * or \c NR_HUGE if no snap point was found.
213  *
214  * \pre d \81â\89\81  (0, 0).
215  */
216 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
217                                 NR::Point &req, NR::Point const &d,
218                                 std::list<SPItem const *> const &it)
220     g_assert(nv != NULL);
221     g_assert(SP_IS_NAMEDVIEW(nv));
223     SPNamedView::SnapperList snappers = nv->getSnappers();
225     NR::Coord best = NR_HUGE;
226     for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
227         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it);
228         if (s.getDistance() < best) {
229             req = s.getPoint();
230             best = s.getDistance();
231         }
232     }
234     return best;
238 /*
239  * functions for lists of points
240  *
241  * All functions take a list of NR::Point and parameter indicating the proposed transformation.
242  * They return the updated transformation parameter.
243  */
245 /**
246  * Snap list of points in one dimension.
247  * \return Coordinate difference.
248  */
249 std::pair<NR::Coord, bool> namedview_dim_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
250                                                    const std::vector<NR::Point> &p,
251                                                    NR::Coord const dx, NR::Dim2 const dim,
252                                                    std::list<SPItem const *> const &it
253                                                    )
255     NR::Coord dist = NR_HUGE;
256     NR::Coord xdist = dx;
258     SnapManager const m(nv);
260     if (m.willSnapSomething()) {
261         for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
262             NR::Point q = *i;
263             NR::Coord const pre = q[dim];
264             q[dim] += dx;
265             NR::Coord const d = namedview_dim_snap(nv, t, q, dim, it);
266             if (d < dist) {
267                 xdist = q[dim] - pre;
268                 dist = d;
269             }
270         }
271     }
273     return std::make_pair(xdist, dist < NR_HUGE);
276 /**
277  * Snap list of points in two dimensions.
278  */
279 std::pair<double, bool> namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
280                                                    const std::vector<NR::Point> &p, NR::Point const &norm,
281                                                    NR::scale const &s, std::list<SPItem const *> const &it)
283     using NR::X;
284     using NR::Y;
286     SnapManager const m(nv);
288     if (m.willSnapSomething() == false) {
289         return std::make_pair(s[X], false);
290     }
292     NR::Coord dist = NR_HUGE;
293     double ratio = fabs(s[X]);
294     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
295         NR::Point const &q = *i;
296         NR::Point check = ( q - norm ) * s + norm;
297         if (NR::LInfty( q - norm ) > MIN_DIST_NORM) {
298             NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it);
299             if (d < dist) {
300                 dist = d;
301                 NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] )  >
302                                               fabs( q[Y] - norm[Y] ) )
303                                             ? X
304                                             : Y );
305                 ratio = ( ( check[dominant] - norm[dominant] )
306                           / ( q[dominant] - norm[dominant] ) );
307             }
308         }
309     }
311     return std::make_pair(ratio, dist < NR_HUGE);
315 /**
316  * Try to snap points in \a p after they have been scaled by \a sx with respect to
317  * the origin \a norm.  The best snap is the one that changes the scale least.
318  *
319  * \return Pair containing snapped scale and a flag which is true if a snap was made.
320  */
321 std::pair<double, bool> namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t,
322                                                       const std::vector<NR::Point> &p, NR::Point const &norm,
323                                                       double const sx, NR::Dim2 dim,
324                                                       std::list<const SPItem *> const &it)
326     SnapManager const m(nv);
327     if (m.willSnapSomething() == false) {
328         return std::make_pair(sx, false);
329     }
331     g_assert(dim < 2);
333     NR::Coord dist = NR_HUGE;
334     double scale = sx;
336     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
337         NR::Point q = *i;
338         NR::Point check = q;
340         /* Scaled version of the point we are looking at */
341         check[dim] = (sx * (q - norm) + norm)[dim];
343         if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) {
344             /* Snap this point */
345             const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it);
346             /* Work out the resulting scale factor */
347             double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]);
349             if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) {
350                 /* This is either the first point, or the snapped scale
351                 ** is the closest yet to the original.
352                 */
353                 scale = snapped_scale;
354                 dist = d;
355             }
356         }
357     }
359     return std::make_pair(scale, dist < NR_HUGE);
362 /**
363  * Try to snap points after they have been skewed.
364  */
365 double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t,
366                                     const std::vector<NR::Point> &p, NR::Point const &norm,
367                                     double const sx, NR::Dim2 const dim)
369     SnapManager const m(nv);
371     if (m.willSnapSomething() == false) {
372         return sx;
373     }
375     g_assert(dim < 2);
377     gdouble dist = NR_HUGE;
378     gdouble skew = sx;
380     for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
381         NR::Point q = *i;
382         NR::Point check = q;
383         // apply shear
384         check[dim] += sx * (q[!dim] - norm[!dim]);
385         if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) {
386             const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL);
387             if (d < fabs (dist)) {
388                 dist = d;
389                 skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]);
390             }
391         }
392     }
394     return skew;
397 /*
398   Local Variables:
399   mode:c++
400   c-file-style:"stroustrup"
401   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
402   indent-tabs-mode:nil
403   fill-column:99
404   End:
405 */
406 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :