Code

improve streaming
[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) 2006      Johan Engelen <johan@shouraizou.nl>
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 /**
25  *  Construct a SnapManager for a SPNamedView.
26  *
27  *  \param v `Owning' SPNamedView.
28  */
30 SnapManager::SnapManager(SPNamedView const *v) : _named_view(v), grid(v, 0), axonomgrid(v, 0), guide(v, 0), object(v, 0)
31 {
33 }
36 /**
37  *  \return List of snappers that we use.
38  */
40 SnapManager::SnapperList SnapManager::getSnappers() const
41 {
42     SnapManager::SnapperList s;
43     if (_named_view->gridtype == 0) {
44       s.push_back(&grid);
45     } else {
46       s.push_back(&axonomgrid);
47     }
48     s.push_back(&guide);
49     s.push_back(&object);
50     return s;
51 }
53 /**
54  * \return true if one of the snappers will try to snap something.
55  */
57 bool SnapManager::willSnapSomething() const
58 {
59     SnapperList const s = getSnappers();
60     SnapperList::const_iterator i = s.begin();
61     while (i != s.end() && (*i)->willSnapSomething() == false) {
62         i++;
63     }
65     return (i != s.end());
66 }
69 /**
70  *  Try to snap a point to any interested snappers.
71  *
72  *  \param t Type of point.
73  *  \param p Point.
74  *  \param it Item to ignore when snapping.
75  *  \return Snapped point.
76  */
78 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
79                                              NR::Point const &p,
80                                              SPItem const *it) const
82 {
83     std::list<SPItem const *> lit;
84     lit.push_back(it);
85     return freeSnap(t, p, lit);
86 }
89 /**
90  *  Try to snap a point to any interested snappers.
91  *
92  *  \param t Type of point.
93  *  \param p Point.
94  *  \param it List of items to ignore when snapping.
95  *  \return Snapped point.
96  */
98 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
99                                              NR::Point const &p,
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)->freeSnap(t, p, it);
107         if (s.getDistance() < r.getDistance()) {
108             r = s;
109         }
110     }
112     return r;
116 /**
117  *  Try to snap a point to any interested snappers.  A snap will only occur along
118  *  a line described by a Inkscape::Snapper::ConstraintLine.
119  *
120  *  \param t Type of point.
121  *  \param p Point.
122  *  \param c Constraint line.
123  *  \param it Item to ignore when snapping.
124  *  \return Snapped point.
125  */
127 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
128                                                     NR::Point const &p,
129                                                     Inkscape::Snapper::ConstraintLine const &c,
130                                                     SPItem const *it) const
132     std::list<SPItem const *> lit;
133     lit.push_back(it);
134     return constrainedSnap(t, p, c, lit);
139 /**
140  *  Try to snap a point to any interested snappers.  A snap will only occur along
141  *  a line described by a Inkscape::Snapper::ConstraintLine.
142  *
143  *  \param t Type of point.
144  *  \param p Point.
145  *  \param c Constraint line.
146  *  \param it List of items to ignore when snapping.
147  *  \return Snapped point.
148  */
150 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
151                                                     NR::Point const &p,
152                                                     Inkscape::Snapper::ConstraintLine const &c,
153                                                     std::list<SPItem const *> const &it) const
155     Inkscape::SnappedPoint r(p, NR_HUGE);
157     SnapperList const snappers = getSnappers();
158     for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
159         Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
160         if (s.getDistance() < r.getDistance()) {
161             r = s;
162         }
163     }
165     return r;
170 /**
171  *  Main internal snapping method, which is called by the other, friendlier, public
172  *  methods.  It's a bit hairy as it has lots of parameters, but it saves on a lot
173  *  of duplicated code.
174  *
175  *  \param type Type of points being snapped.
176  *  \param points List of points to snap.
177  *  \param ignore List of items to ignore while snapping.
178  *  \param constrained true if the snap is constrained.
179  *  \param constraint Constraint line to use, if `constrained' is true, otherwise undefined.
180  *  \param transformation_type Type of transformation to apply to points before trying to snap them.
181  *  \param transformation Description of the transformation; details depend on the type.
182  *  \param origin Origin of the transformation, if applicable.
183  *  \param dim Dimension of the transformation, if applicable.
184  *  \param uniform true if the transformation should be uniform, if applicable.
185  */
187 std::pair<NR::Point, bool> SnapManager::_snapTransformed(
188     Inkscape::Snapper::PointType type,
189     std::vector<NR::Point> const &points,
190     std::list<SPItem const *> const &ignore,
191     bool constrained,
192     Inkscape::Snapper::ConstraintLine const &constraint,
193     Transformation transformation_type,
194     NR::Point const &transformation,
195     NR::Point const &origin,
196     NR::Dim2 dim,
197     bool uniform) const
199     /* We have a list of points, which we are proposing to transform in some way.  We need to see
200     ** if any of these points, when transformed, snap to anything.  If they do, we return the
201     ** appropriate transformation with `true'; otherwise we return the original scale with `false'.
202     */
204     /* Quick check to see if we have any snappers that are enabled */
205     if (willSnapSomething() == false) {
206         return std::make_pair(transformation, false);
207     }
209     /* The current best transformation */
210     NR::Point best_transformation = transformation;
211     
212     /* The current best metric for the best transformation; lower is better, NR_HUGE
213     ** means that we haven't snapped anything.
214     */
215     double best_metric = NR_HUGE;
217     for (std::vector<NR::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
219         /* Work out the transformed version of this point */
220         NR::Point transformed;
221         switch (transformation_type) {
222             case TRANSLATION:
223                 transformed = *i + transformation;
224                 break;
225             case SCALE:
226                 transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin;
227                 break;
228             case STRETCH:
229             {
230                 NR::scale s(1, 1);
231                 if (uniform)
232                     s[NR::X] = s[NR::Y] = transformation[dim];
233                 else {
234                     s[dim] = transformation[dim];
235                     s[1 - dim] = 1;
236                 }
237                 transformed = ((*i - origin) * s) + origin;
238                 break;
239             }
240             case SKEW:
241                 transformed = *i;
242                 transformed[dim] += transformation[dim] * ((*i)[1 - dim] - origin[1 - dim]);
243                 break;
244             default:
245                 g_assert_not_reached();
246         }
247         
248         /* Snap it */
249         Inkscape::SnappedPoint const snapped = constrained ?
250             constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore);
252         if (snapped.getDistance() < NR_HUGE) {
253             /* We snapped.  Find the transformation that describes where the snapped point has
254             ** ended up, and also the metric for this transformation.
255             */
256             NR::Point result;
257             NR::Coord metric;
258             switch (transformation_type) {
259                 case TRANSLATION:
260                     result = snapped.getPoint() - *i;
261                     metric = NR::L2(result);
262                     break;
263                 case SCALE:
264                 {
265                     NR::Point const a = (snapped.getPoint() - origin);
266                     NR::Point const b = (*i - origin);
267                     result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
268                     metric = std::abs(NR::L2(result) - NR::L2(transformation));
269                     break;
270                 }
271                 case STRETCH:
272                 {
273                     for (int j = 0; j < 2; j++) {
274                         if (uniform || j == dim) {
275                             result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
276                         } else {
277                             result[j] = 1;
278                         }
279                     }
280                     metric = std::abs(result[dim] - transformation[dim]);
281                     break;
282                 }
283                 case SKEW:
284                     result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
285                     metric = std::abs(result[dim] - transformation[dim]);
286                     break;
287                 default:
288                     g_assert_not_reached();
289             }
291             /* Note it if it's the best so far */
292             if (metric < best_metric && metric != 0) {
293                 best_transformation = result;
294                 best_metric = metric;
295             }
296         }
297     }
298         
299     return std::make_pair(best_transformation, best_metric < NR_HUGE);
303 /**
304  *  Try to snap a list of points to any interested snappers after they have undergone
305  *  a translation.
306  *
307  *  \param t Type of points.
308  *  \param p Points.
309  *  \param it List of items to ignore when snapping.
310  *  \param tr Proposed translation.
311  *  \return Snapped translation, if a snap occurred, and a flag indicating whether a snap occurred.
312  */
314 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
315                                                             std::vector<NR::Point> const &p,
316                                                             std::list<SPItem const *> const &it,
317                                                             NR::Point const &tr) const
319     return _snapTransformed(
320         t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(), NR::X, false
321         );
325 /**
326  *  Try to snap a list of points to any interested snappers after they have undergone a
327  *  translation.  A snap will only occur along a line described by a
328  *  Inkscape::Snapper::ConstraintLine.
329  *
330  *  \param t Type of points.
331  *  \param p Points.
332  *  \param it List of items to ignore when snapping.
333  *  \param c Constraint line.
334  *  \param tr Proposed translation.
335  *  \return Snapped translation, if a snap occurred, and a flag indicating whether a snap occurred.
336  */
338 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
339                                                                    std::vector<NR::Point> const &p,
340                                                                    std::list<SPItem const *> const &it,
341                                                                    Inkscape::Snapper::ConstraintLine const &c,
342                                                                    NR::Point const &tr) const
344     return _snapTransformed(
345         t, p, it, true, c, TRANSLATION, tr, NR::Point(), NR::X, false
346         );
350 /**
351  *  Try to snap a list of points to any interested snappers after they have undergone
352  *  a scale.
353  *
354  *  \param t Type of points.
355  *  \param p Points.
356  *  \param it List of items to ignore when snapping.
357  *  \param s Proposed scale.
358  *  \param o Origin of proposed scale.
359  *  \return Snapped scale, if a snap occurred, and a flag indicating whether a snap occurred.
360  */
362 std::pair<NR::scale, bool> SnapManager::freeSnapScale(Inkscape::Snapper::PointType t,
363                                                       std::vector<NR::Point> const &p,
364                                                       std::list<SPItem const *> const &it,
365                                                       NR::scale const &s,
366                                                       NR::Point const &o) const
368     return _snapTransformed(
369         t, p, it, false, NR::Point(), SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
370         );
374 /**
375  *  Try to snap a list of points to any interested snappers after they have undergone
376  *  a scale.  A snap will only occur along a line described by a
377  *  Inkscape::Snapper::ConstraintLine.
378  *
379  *  \param t Type of points.
380  *  \param p Points.
381  *  \param it List of items to ignore when snapping.
382  *  \param s Proposed scale.
383  *  \param o Origin of proposed scale.
384  *  \return Snapped scale, if a snap occurred, and a flag indicating whether a snap occurred.
385  */
387 std::pair<NR::scale, bool> SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t,
388                                                              std::vector<NR::Point> const &p,
389                                                              std::list<SPItem const *> const &it,
390                                                              Inkscape::Snapper::ConstraintLine const &c,
391                                                              NR::scale const &s,
392                                                              NR::Point const &o) const
394     return _snapTransformed(
395         t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
396         );
400 /**
401  *  Try to snap a list of points to any interested snappers after they have undergone
402  *  a stretch.
403  *
404  *  \param t Type of points.
405  *  \param p Points.
406  *  \param it List of items to ignore when snapping.
407  *  \param s Proposed stretch.
408  *  \param o Origin of proposed stretch.
409  *  \param d Dimension in which to apply proposed stretch.
410  *  \param u true if the stretch should be uniform (ie to be applied equally in both dimensions)
411  *  \return Snapped stretch, if a snap occurred, and a flag indicating whether a snap occurred.
412  */
414 std::pair<NR::Coord, bool> SnapManager::freeSnapStretch(Inkscape::Snapper::PointType t,
415                                                         std::vector<NR::Point> const &p,
416                                                         std::list<SPItem const *> const &it,
417                                                         NR::Coord const &s,
418                                                         NR::Point const &o,
419                                                         NR::Dim2 d,
420                                                         bool u) const
422    std::pair<NR::Point, bool> const r = _snapTransformed(
423         t, p, it, false, NR::Point(), STRETCH, NR::Point(s, s), o, d, u
424         );
426    return std::make_pair(r.first[d], r.second);
430 /**
431  *  Try to snap a list of points to any interested snappers after they have undergone
432  *  a skew.
433  *
434  *  \param t Type of points.
435  *  \param p Points.
436  *  \param it List of items to ignore when snapping.
437  *  \param s Proposed skew.
438  *  \param o Origin of proposed skew.
439  *  \param d Dimension in which to apply proposed skew.
440  *  \return Snapped skew, if a snap occurred, and a flag indicating whether a snap occurred.
441  */
443 std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointType t,
444                                                      std::vector<NR::Point> const &p,
445                                                      std::list<SPItem const *> const &it,
446                                                      NR::Coord const &s,
447                                                      NR::Point const &o,
448                                                      NR::Dim2 d) const
450    std::pair<NR::Point, bool> const r = _snapTransformed(
451         t, p, it, false, NR::Point(), SKEW, NR::Point(s, s), o, d, false
452         );
454    return std::make_pair(r.first[d], r.second);
457 /*
458   Local Variables:
459   mode:c++
460   c-file-style:"stroustrup"
461   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
462   indent-tabs-mode:nil
463   fill-column:99
464   End:
465 */
466 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :