Code

Small comment cleanups.
[inkscape.git] / src / snap.cpp
1 #define __SP_DESKTOP_SNAP_C__
3 /**
4  * \file snap.cpp
5  * \brief SnapManager class.
6  *
7  * Authors:
8  *   Lauris Kaplinski <lauris@kaplinski.com>
9  *   Frank Felfe <innerspace@iname.com>
10  *   Carl Hetherington <inkscape@carlh.net>
11  *
12  * Copyright (C) 1999-2002 Authors
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 #include "sp-namedview.h"
18 #include "snap.h"
19 #include <libnr/nr-point-fns.h>
20 #include <libnr/nr-scale-ops.h>
21 #include <libnr/nr-values.h>
23 /**
24  *  Construct a SnapManager for a SPNamedView.
25  *
26  *  \param v `Owning' SPNamedView.
27  */
29 SnapManager::SnapManager(SPNamedView const *v) : grid(v, 0), guide(v, 0), object(v, 0)
30 {
32 }
35 /**
36  *  \return List of snappers that we use.
37  */
39 SnapManager::SnapperList SnapManager::getSnappers() const
40 {
41     SnapManager::SnapperList s;
42     s.push_back(&grid);
43     s.push_back(&guide);
44     s.push_back(&object);
45     return s;
46 }
48 /**
49  * \return true if one of the snappers will try to snap something.
50  */
52 bool SnapManager::willSnapSomething() const
53 {
54     SnapperList const s = getSnappers();
55     SnapperList::const_iterator i = s.begin();
56     while (i != s.end() && (*i)->willSnapSomething() == false) {
57         i++;
58     }
60     return (i != s.end());
61 }
64 /**
65  *  Try to snap a point to any interested snappers.
66  *
67  *  \param t Type of point.
68  *  \param p Point.
69  *  \param it Item to ignore when snapping.
70  *  \return Snapped point.
71  */
73 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
74                                              NR::Point const &p,
75                                              SPItem const *it) const
77 {
78     std::list<SPItem const *> lit;
79     lit.push_back(it);
80     return freeSnap(t, p, lit);
81 }
84 /**
85  *  Try to snap a point to any interested snappers.
86  *
87  *  \param t Type of point.
88  *  \param p Point.
89  *  \param it List of items to ignore when snapping.
90  *  \return Snapped point.
91  */
93 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
94                                              NR::Point const &p,
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)->freeSnap(t, p, it);
102         if (s.getDistance() < r.getDistance()) {
103             r = s;
104         }
105     }
107     return r;
111 /**
112  *  Try to snap a point to any interested snappers.  A snap will only occur along
113  *  a line described by a Inkscape::Snapper::ConstraintLine.
114  *
115  *  \param t Type of point.
116  *  \param p Point.
117  *  \param c Constraint line.
118  *  \param it Item to ignore when snapping.
119  *  \return Snapped point.
120  */
122 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
123                                                     NR::Point const &p,
124                                                     Inkscape::Snapper::ConstraintLine const &c,
125                                                     SPItem const *it) const
127     std::list<SPItem const *> lit;
128     lit.push_back(it);
129     return constrainedSnap(t, p, c, lit);
134 /**
135  *  Try to snap a point to any interested snappers.  A snap will only occur along
136  *  a line described by a Inkscape::Snapper::ConstraintLine.
137  *
138  *  \param t Type of point.
139  *  \param p Point.
140  *  \param c Constraint line.
141  *  \param it List of items to ignore when snapping.
142  *  \return Snapped point.
143  */
145 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
146                                                     NR::Point const &p,
147                                                     Inkscape::Snapper::ConstraintLine const &c,
148                                                     std::list<SPItem const *> const &it) const
150     Inkscape::SnappedPoint r(p, NR_HUGE);
152     SnapperList const snappers = getSnappers();
153     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
154         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
155         if (s.getDistance() < r.getDistance()) {
156             r = s;
157         }
158     }
160     return r;
165 /**
166  *  Main internal snapping method, which is called by the other, friendlier, public
167  *  methods.  It's a bit hairy as it has lots of parameters, but it saves on a lot
168  *  of duplicated code.
169  *
170  *  \param type Type of points being snapped.
171  *  \param points List of points to snap.
172  *  \param ignore List of items to ignore while snapping.
173  *  \param constrained true if the snap is constrained.
174  *  \param constraint Constraint line to use, if `constrained' is true, otherwise undefined.
175  *  \param transformation_type Type of transformation to apply to points before trying to snap them.
176  *  \param transformation Description of the transformation; details depend on the type.
177  *  \param origin Origin of the transformation, if applicable.
178  *  \param dim Dimension of the transformation, if applicable.
179  *  \param uniform true if the transformation should be uniform, if applicable.
180  */
182 std::pair<NR::Point, bool> SnapManager::_snapTransformed(
183     Inkscape::Snapper::PointType type,
184     std::vector<NR::Point> const &points,
185     std::list<SPItem const *> const &ignore,
186     bool constrained,
187     Inkscape::Snapper::ConstraintLine const &constraint,
188     Transformation transformation_type,
189     NR::Point const &transformation,
190     NR::Point const &origin,
191     NR::Dim2 dim,
192     bool uniform) const
194     /* We have a list of points, which we are proposing to transform in some way.  We need to see
195     ** if any of these points, when transformed, snap to anything.  If they do, we return the
196     ** appropriate transformation with `true'; otherwise we return the original scale with `false'.
197     */
199     /* Quick check to see if we have any snappers that are enabled */
200     if (willSnapSomething() == false) {
201         return std::make_pair(transformation, false);
202     }
204     /* The current best transformation */
205     NR::Point best_transformation = transformation;
206     
207     /* The current best metric for the best transformation; lower is better, NR_HUGE
208     ** means that we haven't snapped anything.
209     */
210     double best_metric = NR_HUGE;
212     for (std::vector<NR::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
214         /* Work out the transformed version of this point */
215         NR::Point transformed;
216         switch (transformation_type) {
217             case TRANSLATION:
218                 transformed = *i + transformation;
219                 break;
220             case SCALE:
221                 transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin;
222                 break;
223             case STRETCH:
224             {
225                 NR::scale s(1, 1);
226                 if (uniform)
227                     s[NR::X] = s[NR::Y] = transformation[dim];
228                 else {
229                     s[dim] = transformation[dim];
230                     s[1 - dim] = 1;
231                 }
232                 transformed = ((*i - origin) * s) + origin;
233                 break;
234             }
235             case SKEW:
236                 transformed = *i;
237                 transformed[dim] += transformation[dim] * ((*i)[1 - dim] - origin[1 - dim]);
238                 break;
239             default:
240                 g_assert_not_reached();
241         }
242         
243         /* Snap it */
244         Inkscape::SnappedPoint const snapped = constrained ?
245             constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore);
247         if (snapped.getDistance() < NR_HUGE) {
248             /* We snapped.  Find the transformation that describes where the snapped point has
249             ** ended up, and also the metric for this transformation.
250             */
251             NR::Point result;
252             NR::Coord metric;
253             switch (transformation_type) {
254                 case TRANSLATION:
255                     result = snapped.getPoint() - *i;
256                     metric = NR::L2(result);
257                     break;
258                 case SCALE:
259                 {
260                     NR::Point const a = (snapped.getPoint() - origin);
261                     NR::Point const b = (*i - origin);
262                     result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
263                     metric = std::abs(NR::L2(result) - NR::L2(transformation));
264                     break;
265                 }
266                 case STRETCH:
267                 {
268                     for (int j = 0; j < 2; j++) {
269                         if (uniform || j == dim) {
270                             result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
271                         } else {
272                             result[j] = 1;
273                         }
274                     }
275                     metric = std::abs(result[dim] - transformation[dim]);
276                     break;
277                 }
278                 case SKEW:
279                     result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
280                     metric = std::abs(result[dim] - transformation[dim]);
281                     break;
282                 default:
283                     g_assert_not_reached();
284             }
286             /* Note it if it's the best so far */
287             if (metric < best_metric && metric != 0) {
288                 best_transformation = result;
289                 best_metric = metric;
290             }
291         }
292     }
293         
294     return std::make_pair(best_transformation, best_metric < NR_HUGE);
298 /**
299  *  Try to snap a list of points to any interested snappers after they have undergone
300  *  a translation.
301  *
302  *  \param t Type of points.
303  *  \param p Points.
304  *  \param it List of items to ignore when snapping.
305  *  \param tr Proposed translation.
306  *  \return Snapped translation, if a snap occurred, and a flag indicating whether a snap occurred.
307  */
309 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
310                                                             std::vector<NR::Point> const &p,
311                                                             std::list<SPItem const *> const &it,
312                                                             NR::Point const &tr) const
314     return _snapTransformed(
315         t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(), NR::X, false
316         );
320 /**
321  *  Try to snap a list of points to any interested snappers after they have undergone a
322  *  translation.  A snap will only occur along a line described by a
323  *  Inkscape::Snapper::ConstraintLine.
324  *
325  *  \param t Type of points.
326  *  \param p Points.
327  *  \param it List of items to ignore when snapping.
328  *  \param c Constraint line.
329  *  \param tr Proposed translation.
330  *  \return Snapped translation, if a snap occurred, and a flag indicating whether a snap occurred.
331  */
333 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
334                                                                    std::vector<NR::Point> const &p,
335                                                                    std::list<SPItem const *> const &it,
336                                                                    Inkscape::Snapper::ConstraintLine const &c,
337                                                                    NR::Point const &tr) const
339     return _snapTransformed(
340         t, p, it, true, c, TRANSLATION, tr, NR::Point(), NR::X, false
341         );
345 /**
346  *  Try to snap a list of points to any interested snappers after they have undergone
347  *  a scale.
348  *
349  *  \param t Type of points.
350  *  \param p Points.
351  *  \param it List of items to ignore when snapping.
352  *  \param s Proposed scale.
353  *  \param o Origin of proposed scale.
354  *  \return Snapped scale, if a snap occurred, and a flag indicating whether a snap occurred.
355  */
357 std::pair<NR::scale, bool> SnapManager::freeSnapScale(Inkscape::Snapper::PointType t,
358                                                       std::vector<NR::Point> const &p,
359                                                       std::list<SPItem const *> const &it,
360                                                       NR::scale const &s,
361                                                       NR::Point const &o) const
363     return _snapTransformed(
364         t, p, it, false, NR::Point(), SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
365         );
369 /**
370  *  Try to snap a list of points to any interested snappers after they have undergone
371  *  a scale.  A snap will only occur along a line described by a
372  *  Inkscape::Snapper::ConstraintLine.
373  *
374  *  \param t Type of points.
375  *  \param p Points.
376  *  \param it List of items to ignore when snapping.
377  *  \param s Proposed scale.
378  *  \param o Origin of proposed scale.
379  *  \return Snapped scale, if a snap occurred, and a flag indicating whether a snap occurred.
380  */
382 std::pair<NR::scale, bool> SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t,
383                                                              std::vector<NR::Point> const &p,
384                                                              std::list<SPItem const *> const &it,
385                                                              Inkscape::Snapper::ConstraintLine const &c,
386                                                              NR::scale const &s,
387                                                              NR::Point const &o) const
389     return _snapTransformed(
390         t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
391         );
395 /**
396  *  Try to snap a list of points to any interested snappers after they have undergone
397  *  a stretch.
398  *
399  *  \param t Type of points.
400  *  \param p Points.
401  *  \param it List of items to ignore when snapping.
402  *  \param s Proposed stretch.
403  *  \param o Origin of proposed stretch.
404  *  \param d Dimension in which to apply proposed stretch.
405  *  \param u true if the stretch should be uniform (ie to be applied equally in both dimensions)
406  *  \return Snapped stretch, if a snap occurred, and a flag indicating whether a snap occurred.
407  */
409 std::pair<NR::Coord, bool> SnapManager::freeSnapStretch(Inkscape::Snapper::PointType t,
410                                                         std::vector<NR::Point> const &p,
411                                                         std::list<SPItem const *> const &it,
412                                                         NR::Coord const &s,
413                                                         NR::Point const &o,
414                                                         NR::Dim2 d,
415                                                         bool u) const
417    std::pair<NR::Point, bool> const r = _snapTransformed(
418         t, p, it, false, NR::Point(), STRETCH, NR::Point(s, s), o, d, u
419         );
421    return std::make_pair(r.first[d], r.second);
425 /**
426  *  Try to snap a list of points to any interested snappers after they have undergone
427  *  a skew.
428  *
429  *  \param t Type of points.
430  *  \param p Points.
431  *  \param it List of items to ignore when snapping.
432  *  \param s Proposed skew.
433  *  \param o Origin of proposed skew.
434  *  \param d Dimension in which to apply proposed skew.
435  *  \return Snapped skew, if a snap occurred, and a flag indicating whether a snap occurred.
436  */
438 std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointType t,
439                                                      std::vector<NR::Point> const &p,
440                                                      std::list<SPItem const *> const &it,
441                                                      NR::Coord const &s,
442                                                      NR::Point const &o,
443                                                      NR::Dim2 d) const
445    std::pair<NR::Point, bool> const r = _snapTransformed(
446         t, p, it, false, NR::Point(), SKEW, NR::Point(s, s), o, d, false
447         );
449    return std::make_pair(r.first[d], r.second);
452 /*
453   Local Variables:
454   mode:c++
455   c-file-style:"stroustrup"
456   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
457   indent-tabs-mode:nil
458   fill-column:99
459   End:
460 */
461 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :