snap.cpp revision 754eb8f0aee44ed5d465f1f7b49dcecd6787ec82
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \brief SnapManager class.
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Authors:
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Lauris Kaplinski <lauris@kaplinski.com>
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * Frank Felfe <innerspace@iname.com>
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * Nathan Hurst <njh@njhurst.com>
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Carl Hetherington <inkscape@carlh.net>
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Diederik van Lierop <mail@diedenrezi.nl>
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Copyright (C) 2006-2007 Johan Engelen <johan@shouraizou.nl>
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Copyrigth (C) 2004 Nathan Hurst
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Copyright (C) 1999-2010 Authors
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Released under GNU GPL, read the file 'COPYING' for more information
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Construct a SnapManager for a SPNamedView.
072916d0ef7dccd696b59381f50bcf776abccefbjohanengelen * \param v `Owning' SPNamedView.
3d0482af18ffb591c1d8ddecf516629e1bcd2ae4cilix * \brief Return a list of snappers
b320a8d186114a5122ddc3afbe95110eb6cb10cecilix * Inkscape snaps to objects, grids, and guides. For each of these snap targets a
044d712d4d03f8354962d54e47cfac2346a69ccccilix * separate class is used, which has been derived from the base Snapper class. The
61cfd957cd023c4f432ea0c7307784a56bf978e9cilix * getSnappers() method returns a list of pointers to instances of this class. This
2f5c0701b333a695eedb1680beb1adf95c0723dacilix * list contains exactly one instance of the guide snapper and of the object snapper
add2ffae3c4686b50d888775bbdf083a4726a210johanengelen * class, but any number of grid snappers (because each grid has its own snapper
e54ce05030e6aab675331e18f46f029f55ed1bf0cilix * instance)
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \return List of snappers that we use.
0563fd55cbad59e8a878e6d4cbbdd8e47f74488djohanengelen * \brief Return a list of gridsnappers
70eb1fc448cb08acf3468f80fa2296c03b32afd2cilix * Each grid has its own instance of the snapper class. This way snapping can
c169f6cddd2da06cfb761339f445bbd8866f72a8buliabyak * be enabled per grid individually. A list will be returned containing the
0563fd55cbad59e8a878e6d4cbbdd8e47f74488djohanengelen * pointers to these instances, but only for grids that are being displayed
0cc5b8d2f7b87c4222ee3662071bef1cb1f22b06bgk * and for which snapping is enabled.
f4db63be4e929f4706410914295deccaceea19cdcilix * \return List of gridsnappers that we use.
61cfd957cd023c4f432ea0c7307784a56bf978e9cilix if (_desktop && _desktop->gridsEnabled() && snapprefs.getSnapToGrids()) {
2f5c0701b333a695eedb1680beb1adf95c0723dacilix for ( GSList const *l = _named_view->grids; l != NULL; l = l->next) {
add2ffae3c4686b50d888775bbdf083a4726a210johanengelen Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
8c39cbeab9949a0a7d6ae66b768a7352019e42f8johanengelen * \brief Return true if any snapping might occur, whether its to grids, guides or objects
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Each snapper instance handles its own snapping target, e.g. grids, guides or
29f9623ba77fc735b89765ae3a13e0c06aabafcecilix * objects. This method iterates through all these snapper instances and returns
29f9623ba77fc735b89765ae3a13e0c06aabafcecilix * true if any of the snappers might possible snap, considering only the relevant
29f9623ba77fc735b89765ae3a13e0c06aabafcecilix * snapping preferences.
92fe3142613d000eff89db8a983b3b18b14eee79johanengelen * \return true if one of the snappers will try to snap to something.
dc98accfae7a38326b92d74fa4330ac8ccb5b778jfbarraud if ( !snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally() ) {
92fe3142613d000eff89db8a983b3b18b14eee79johanengelen return false;
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm while (i != s.end() && (*i)->ThisSnapperMightSnap() == false) {
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm return (i != s.end());
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \return true if one of the grids might be snapped to.
0563fd55cbad59e8a878e6d4cbbdd8e47f74488djohanengelen if ( !snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally() ) {
0563fd55cbad59e8a878e6d4cbbdd8e47f74488djohanengelen return false;
8d9f5d586a04809427ce1df284a5720112177991cilix while (i != s.end() && (*i)->ThisSnapperMightSnap() == false) {
c169f6cddd2da06cfb761339f445bbd8866f72a8buliabyak return (i != s.end());
6f4a90e526af850ffc36064f58f09c190f3b633fjohanengelen * \brief Try to snap a point to grids, guides or objects.
f4db63be4e929f4706410914295deccaceea19cdcilix * Try to snap a point to grids, guides or objects, in two degrees-of-freedom,
f4db63be4e929f4706410914295deccaceea19cdcilix * i.e. snap in any direction on the two dimensional canvas to the nearest
f4db63be4e929f4706410914295deccaceea19cdcilix * snap target. freeSnapReturnByRef() is equal in snapping behavior to
ab99111a42436818e6902e044c8f3af2b724263bcilix * freeSnap(), but the former returns the snapped point trough the referenced
ab99111a42436818e6902e044c8f3af2b724263bcilix * parameter p. This parameter p initially contains the position of the snap
ab99111a42436818e6902e044c8f3af2b724263bcilix * source and will we overwritten by the target position if snapping has occurred.
76db360f5f052775326e6d406b9e1e9e2966e11acilix * This makes snapping transparent to the calling code. If this is not desired
76db360f5f052775326e6d406b9e1e9e2966e11acilix * because either the calling code must know whether snapping has occurred, or
b0c42c0dfcd02cc05126371948489a5a88b2e4b3cilix * because the original position should not be touched, then freeSnap() should be
3d0482af18ffb591c1d8ddecf516629e1bcd2ae4cilix * called instead.
64aee804a6a47424f7994e60558351b8cf2ea4dbcilix * 1) SnapManager::setup() must have been called before calling this method,
64aee804a6a47424f7994e60558351b8cf2ea4dbcilix * but only once for a set of points
64aee804a6a47424f7994e60558351b8cf2ea4dbcilix * 2) Only to be used when a single source point is to be snapped; it assumes
b320a8d186114a5122ddc3afbe95110eb6cb10cecilix * that source_num = 0, which is inefficient when snapping sets our source points
b320a8d186114a5122ddc3afbe95110eb6cb10cecilix * \param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred
044d712d4d03f8354962d54e47cfac2346a69ccccilix * \param source_type Detailed description of the source type, will be used by the snap indicator
044d712d4d03f8354962d54e47cfac2346a69ccccilix * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation
2f5c0701b333a695eedb1680beb1adf95c0723dacilix Inkscape::SnappedPoint const s = freeSnap(Inkscape::SnapCandidatePoint(p, source_type), bbox_to_snap);
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \brief Try to snap a point to grids, guides or objects.
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Try to snap a point to grids, guides or objects, in two degrees-of-freedom,
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * i.e. snap in any direction on the two dimensional canvas to the nearest
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * snap target. freeSnap() is equal in snapping behavior to
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * freeSnapReturnByRef(). Please read the comments of the latter for more details
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * PS: SnapManager::setup() must have been called before calling this method,
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * but only once for a set of points
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \param p Source point to be snapped
6656f193fdace606d1b162d6dea0223bc295f0a6cilix * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation
6656f193fdace606d1b162d6dea0223bc295f0a6cilix * \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics
6656f193fdace606d1b162d6dea0223bc295f0a6cilixInkscape::SnappedPoint SnapManager::freeSnap(Inkscape::SnapCandidatePoint const &p,
6656f193fdace606d1b162d6dea0223bc295f0a6cilix return Inkscape::SnappedPoint(p, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false, false);
6656f193fdace606d1b162d6dea0223bc295f0a6cilix for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
6656f193fdace606d1b162d6dea0223bc295f0a6cilix (*i)->freeSnap(sc, p, bbox_to_snap, &_items_to_ignore, _unselected_nodes);
6656f193fdace606d1b162d6dea0223bc295f0a6cilixvoid SnapManager::preSnap(Inkscape::SnapCandidatePoint const &p)
fbb4eb8b63e74d9441220a73a8ca858425be4bd4johanengelen // setup() must have been called before calling this method!
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix _snapindicator = false; // prevent other methods from drawing a snap indicator; we want to control this here
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \brief Snap to the closest multiple of a grid pitch
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * When pasting, we would like to snap to the grid. Problem is that we don't know which
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * nodes were aligned to the grid at the time of copying, so we don't know which nodes
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * to snap. If we'd snap an unaligned node to the grid, previously aligned nodes would
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * become unaligned. That's undesirable. Instead we will make sure that the offset
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * between the source and its pasted copy is a multiple of the grid pitch. If the source
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * was aligned, then the copy will therefore also be aligned.
c90010388b0d4045c26e81c2be28beedcb36c7d3cilix * PS: Whether we really find a multiple also depends on the snapping range! Most users
c90010388b0d4045c26e81c2be28beedcb36c7d3cilix * will have "always snap" enabled though, in which case a multiple will always be found.
c90010388b0d4045c26e81c2be28beedcb36c7d3cilix * PS2: When multiple grids are present then the result will become ambiguous. There is no
c90010388b0d4045c26e81c2be28beedcb36c7d3cilix * way to control to which grid this method will snap.
77a4a003111bd5cfb771d4849801c898aeb889b0cilix * \param t Vector that represents the offset of the pasted copy with respect to the original
77a4a003111bd5cfb771d4849801c898aeb889b0cilix * \return Offset vector after snapping to the closest multiple of a grid pitch
262d0c3f05130d86368d95f110aa8ccab5f83e5ccilixGeom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point const &origin)
262d0c3f05130d86368d95f110aa8ccab5f83e5ccilix if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally())
23d859f2ce09c04ed802cb4912cc9c50f512f0a2bgk bool success = false;
23d859f2ce09c04ed802cb4912cc9c50f512f0a2bgk // It will snap to the grid for which we find the closest snap. This might be a different
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // grid than to which the objects were initially aligned. I don't see an easy way to fix
147c8e03bb214f85cd5906ddc6413c4293c4baa9cilix // this, so when using multiple grids one can get unexpected results
147c8e03bb214f85cd5906ddc6413c4293c4baa9cilix // Cannot use getGridSnappers() because we need both the grids AND their snappers
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // Therefore we iterate through all grids manually
77a4a003111bd5cfb771d4849801c898aeb889b0cilix for (GSList const *l = _named_view->grids; l != NULL; l = l->next) {
77a4a003111bd5cfb771d4849801c898aeb889b0cilix Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // To find the nearest multiple of the grid pitch for a given translation t, we
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // will use the grid snapper. Simply snapping the value t to the grid will do, but
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // only if the origin of the grid is at (0,0). If it's not then compensate for this
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // in the translation t
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // Only the first three parameters are being used for grid snappers
77a4a003111bd5cfb771d4849801c898aeb889b0cilix snapper->freeSnap(sc, Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH),Geom::OptRect(), NULL, NULL);
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // Find the best snap for this grid, including intersections of the grid-lines
77a4a003111bd5cfb771d4849801c898aeb889b0cilix Inkscape::SnappedPoint s = findBestSnap(Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH), sc, false, false, true);
77a4a003111bd5cfb771d4849801c898aeb889b0cilix if (s.getSnapped() && (s.getSnapDistance() < nearest_distance)) {
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // use getSnapDistance() instead of getWeightedDistance() here because the pointer's position
77a4a003111bd5cfb771d4849801c898aeb889b0cilix // doesn't tell us anything about which node to snap
97a20864afec63a0b7bb757b628ee2ae596cf648cilix nearest_multiple = s.getPoint() - to_2geom(grid->origin);
77a4a003111bd5cfb771d4849801c898aeb889b0cilix _desktop->snapindicator->set_new_snaptarget(bestSnappedPoint);
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \brief Try to snap a point along a constraint line to grids, guides or objects.
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Try to snap a point to grids, guides or objects, in only one degree-of-freedom,
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * i.e. snap in a specific direction on the two dimensional canvas to the nearest
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * snap target.
b802808a0226a87371021393c4f1da776aa6a6adjohanengelen * constrainedSnapReturnByRef() is equal in snapping behavior to
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * constrainedSnap(), but the former returns the snapped point trough the referenced
b802808a0226a87371021393c4f1da776aa6a6adjohanengelen * parameter p. This parameter p initially contains the position of the snap
dda97aeba7480d08320ebceecae13b8531db1b81johanengelen * source and will we overwritten by the target position if snapping has occurred.
a2fbdfc8e80d3d1845bf0d5df989726ae2ffd5bfjohanengelen * This makes snapping transparent to the calling code. If this is not desired
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * because either the calling code must know whether snapping has occurred, or
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * because the original position should not be touched, then constrainedSnap() should
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * be called instead.
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * 1) SnapManager::setup() must have been called before calling this method,
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * but only once for a set of points
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * 2) Only to be used when a single source point is to be snapped; it assumes
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * that source_num = 0, which is inefficient when snapping sets our source points
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * \param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * \param source_type Detailed description of the source type, will be used by the snap indicator
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * \param constraint The direction or line along which snapping must occur
46c4893a7458eda6edcd064121bc000634af7a09johanengelen * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation
46c4893a7458eda6edcd064121bc000634af7a09johanengelenvoid SnapManager::constrainedSnapReturnByRef(Geom::Point &p,
46c4893a7458eda6edcd064121bc000634af7a09johanengelen Inkscape::Snapper::SnapConstraint const &constraint,
46c4893a7458eda6edcd064121bc000634af7a09johanengelen Inkscape::SnappedPoint const s = constrainedSnap(Inkscape::SnapCandidatePoint(p, source_type), constraint, bbox_to_snap);
46c4893a7458eda6edcd064121bc000634af7a09johanengelen p = s.getPoint(); // If we didn't snap, then we will return the point projected onto the constraint
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \brief Try to snap a point along a constraint line to grids, guides or objects.
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * Try to snap a point to grids, guides or objects, in only one degree-of-freedom,
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * i.e. snap in a specific direction on the two dimensional canvas to the nearest
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * snap target. constrainedSnap is equal in snapping behavior to
ecda720053ff791e35dae3c5c1177bc225b6cdf1johanengelen * constrainedSnapReturnByRef(). Please read the comments of the latter for more details.
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * PS: SnapManager::setup() must have been called before calling this method,
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * but only once for a set of points
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \param p Source point to be snapped
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \param constraint The direction or line along which snapping must occur
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelenInkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint const &p,
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen Inkscape::Snapper::SnapConstraint const &constraint,
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen // First project the mouse pointer onto the constraint
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen Geom::Point pp = constraint.projection(p.getPoint());
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, NR_HUGE, 0, false, true, false);
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen // Always return point on constraint
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm Inkscape::Preferences *prefs = Inkscape::Preferences::get();
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm if ((prefs->getBool("/options/snapmousepointer/value", false)) && p.isSingleHandle()) {
0fc5ce7045233dae7e15fdc86774370f1b1d73cbjohanengelen // Snapping the mouse pointer instead of the constrained position of the knot allows
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm // to snap to things which don't intersect with the constraint line; this is basically
0fc5ce7045233dae7e15fdc86774370f1b1d73cbjohanengelen // then just a freesnap with the constraint applied afterwards
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm // We'll only to this if we're dragging a single handle, and for example not when transforming an object in the selector tool
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm // only change the snap indicator if we really snapped to something
2d107ef9730aff3f4d776ae0c2f7d983e289ce02joncruz _desktop->snapindicator->set_new_snaptarget(result);
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm // Apply the constraint
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm result.setPoint(constraint.projection(result.getPoint()));
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm (*i)->constrainedSnap(sc, p, bbox_to_snap, constraint, &_items_to_ignore, _unselected_nodes);
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix // only change the snap indicator if we really snapped to something
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix/* See the documentation for constrainedSnap() directly above for more details.
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix * The difference is that multipleConstrainedSnaps() will take a list of constraints instead of a single one,
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix * and will try to snap the SnapCandidatePoint to all of the provided constraints and see which one fits best
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix * \param p Source point to be snapped
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix * \param constraints List of directions or lines along which snapping must occur
5be124ad592f5c71eca838ad2eaac9ffa953605fcilix * \param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation
73d455c08e8062e257dd052d2d690b9300434351cilixInkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandidatePoint const &p,
73d455c08e8062e257dd052d2d690b9300434351cilix std::vector<Inkscape::Snapper::SnapConstraint> const &constraints,
1e944d29efb206f5d0b5d1069cb098e22169d548cilix Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(p.getPoint(), p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, NR_HUGE, 0, false, true, false);
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix Inkscape::Preferences *prefs = Inkscape::Preferences::get();
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix bool snap_mouse = prefs->getBool("/options/snapmousepointer/value", false);
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix for (std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); c != constraints.end(); c++) {
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix // Project the mouse pointer onto the constraint; In case we don't snap then we will
bb78cf2c3a2ee8ea2c98433128556847f03f5799cilix // return the projection onto the constraint, such that the constraint is always enforced
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix // Snapping the mouse pointer instead of the constrained position of the knot allows
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix // to snap to things which don't intersect with the constraint line; this is basically
bb78cf2c3a2ee8ea2c98433128556847f03f5799cilix // then just a freesnap with the constraint applied afterwards
732fb09f9c502000068a77667c3356cbbd5d39d5cilix // We'll only to this if we're dragging a single handle, and for example not when transforming an object in the selector tool
732fb09f9c502000068a77667c3356cbbd5d39d5cilix // Iterate over the constraints
732fb09f9c502000068a77667c3356cbbd5d39d5cilix for (std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); c != constraints.end(); c++) {
732fb09f9c502000068a77667c3356cbbd5d39d5cilix // Try to snap to the constraint
732fb09f9c502000068a77667c3356cbbd5d39d5cilix for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
732fb09f9c502000068a77667c3356cbbd5d39d5cilix (*i)->constrainedSnap(sc, p, bbox_to_snap, *c, &_items_to_ignore,_unselected_nodes);
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix // If "snap_mouse" then we still have to apply the constraint, because so far we only tried a freeSnap
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix for (std::vector<Inkscape::Snapper::SnapConstraint>::const_iterator c = constraints.begin(); c != constraints.end(); c++) {
04c99c338ffdc6e10cb6f5c18f6f06b3f555e8ebcilix // Project the mouse pointer onto the constraint; In case we don't snap then we will
c0cd5511d3b975ebe07d019c1f5528108725e438johanengelen // return the projection onto the constraint, such that the constraint is always enforced
73d455c08e8062e257dd052d2d690b9300434351cilix Geom::Point result_p = (*c).projection(result.getPoint());
73d455c08e8062e257dd052d2d690b9300434351cilix if (c == constraints.begin() || (Geom::L2(result_p - p.getPoint()) < Geom::L2(result_closest - p.getPoint()))) {
c0cd5511d3b975ebe07d019c1f5528108725e438johanengelen // So we didn't snap, but we still need to return a point on one of the constraints
c0cd5511d3b975ebe07d019c1f5528108725e438johanengelen // Find out which of the constraints yielded the closest projection of point p
c0cd5511d3b975ebe07d019c1f5528108725e438johanengelen for (std::vector<Geom::Point>::iterator pp = projections.begin(); pp != projections.end(); pp++) {
c0cd5511d3b975ebe07d019c1f5528108725e438johanengelen if (Geom::L2(*pp - p.getPoint()) < Geom::L2(no_snap.getPoint() - p.getPoint())) {
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \brief Try to snap a point to something at a specific angle
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * When drawing a straight line or modifying a gradient, it will snap to specific angle increments
56542e2b97ec8826cc692153b0e2d4f5ac8ef913johanengelen * if CTRL is being pressed. This method will enforce this angular constraint (even if there is nothing
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * to snap to)
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \param p Source point to be snapped
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * \param p_ref Optional original point, relative to which the angle should be calculated. If empty then
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm * the angle will be calculated relative to the y-axis
56542e2b97ec8826cc692153b0e2d4f5ac8ef913johanengelen * \param snaps Number of angular increments per PI radians; E.g. if snaps = 2 then we will snap every PI/2 = 90 degrees
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrmInkscape::SnappedPoint SnapManager::constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p,
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm unsigned const snaps) const
56542e2b97ec8826cc692153b0e2d4f5ac8ef913johanengelen if (snaps > 0) { // 0 means no angular snapping
56542e2b97ec8826cc692153b0e2d4f5ac8ef913johanengelen // p is at an arbitrary angle. Now we should snap this angle to specific increments.
56542e2b97ec8826cc692153b0e2d4f5ac8ef913johanengelen // For this we'll calculate the closest two angles, one at each side of the current angle
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen Geom::Line y_axis(Geom::Point(0, 0), Geom::Point(0, 1));
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen double angle = Geom::angle_between(y_axis, p_line);
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen angle_offset = Geom::angle_between(y_axis, p_line_ref);
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen double angle_ceil = round_to_upper_multiple_plus(angle, angle_incr, angle_offset);
93bb287e28a818fd5ba61b99d012e0500a49ccf6johanengelen double angle_floor = round_to_lower_multiple_plus(angle, angle_incr, angle_offset);
56542e2b97ec8826cc692153b0e2d4f5ac8ef913johanengelen // We have two angles now. The constrained snapper will try each of them and return the closest
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen // Now do the snapping...
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen std::vector<Inkscape::Snapper::SnapConstraint> constraints;
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_ceil - M_PI/2)));
af8d25189f88abf89cdbe0e180e271c94079624fbuliabyak constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_floor - M_PI/2)));
af8d25189f88abf89cdbe0e180e271c94079624fbuliabyak sp = multipleConstrainedSnaps(p, constraints); // Constraints will always be applied, even if we didn't snap
af8d25189f88abf89cdbe0e180e271c94079624fbuliabyak if (!sp.getSnapped()) { // If we haven't snapped then we only had the constraint applied;
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen sp.setTarget(Inkscape::SNAPTARGET_CONSTRAINED_ANGLE);
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen * \brief Try to snap a point of a guide to another guide or to a node
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen * Try to snap a point of a guide to another guide or to a node in two degrees-
f9504c822b72a774b910958446fd1e730235b7cbjoncruz * of-freedom, i.e. snap in any direction on the two dimensional canvas to the
a0334366488989ef25fb812d7030d298c0917c96johanengelen * nearest snap target. This method is used when dragging or rotating a guide
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen * PS: SnapManager::setup() must have been called before calling this method,
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen * \param p Current position of the point on the guide that is to be snapped; will be overwritten by the position of the snap target if snapping has occurred
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen * \param guide_normal Vector normal to the guide line
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelenvoid SnapManager::guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally()) {
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen if (!(object.ThisSnapperMightSnap() || snapprefs.getSnapToGuides())) {
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen Inkscape::SnapCandidatePoint candidate(p, Inkscape::SNAPSOURCE_GUIDE_ORIGIN);
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen candidate = Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_GUIDE);
fb5a72174252e0e79107dcad3bf5a2bbd73e349cjohanengelen // Snap to nodes
42e99769805c14a5cc01c805faa3c3b03f9dd1c0johanengelen // Snap to guides & grid lines
42e99769805c14a5cc01c805faa3c3b03f9dd1c0johanengelen for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
42e99769805c14a5cc01c805faa3c3b03f9dd1c0johanengelen (*i)->freeSnap(sc, candidate, Geom::OptRect(), NULL, NULL);
a797dcb8e284cab19f60b3eff93a53a62abda263johanengelen Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false, false);
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * \brief Try to snap a point on a guide to the intersection with another guide or a path
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * Try to snap a point on a guide to the intersection of that guide with another
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * guide or with a path. The snapped point will lie somewhere on the guide-line,
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * making this is a constrained snap, i.e. in only one degree-of-freedom.
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * This method is used when dragging the origin of the guide along the guide itself.
ddc251b3cf95b0097b6a5ee39ea132bd4d7d5cbcjohanengelen * PS: SnapManager::setup() must have been called before calling this method,
42e99769805c14a5cc01c805faa3c3b03f9dd1c0johanengelen * \param p Current position of the point on the guide that is to be snapped; will be overwritten by the position of the snap target if snapping has occurred
42ba1b712b7b430669fc49aa9facb439181081becilix * \param guide_normal Vector normal to the guide line
42ba1b712b7b430669fc49aa9facb439181081becilixvoid SnapManager::guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const
42ba1b712b7b430669fc49aa9facb439181081becilix if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally()) {
42ba1b712b7b430669fc49aa9facb439181081becilix if (!(object.ThisSnapperMightSnap() || snapprefs.getSnapToGuides())) {
42ba1b712b7b430669fc49aa9facb439181081becilix Inkscape::SnapCandidatePoint candidate(p, Inkscape::SNAPSOURCE_GUIDE_ORIGIN, Inkscape::SNAPTARGET_UNDEFINED);
42ba1b712b7b430669fc49aa9facb439181081becilix // Snap to nodes or paths
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm Inkscape::Snapper::SnapConstraint cl(guideline.point_on_line, Geom::rot90(guideline.normal_to_line));
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm object.constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL, NULL);
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm // Snap to guides & grid lines
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm (*i)->constrainedSnap(sc, candidate, Geom::OptRect(), cl, NULL, NULL);
f07bfd5a05d43a6d11f7cd442f085149092dea88pjrm Inkscape::SnappedPoint const s = findBestSnap(candidate, sc, false);
* \param points Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \param constrained true if the snap is constrained, e.g. for stretching or for purely horizontal translation.
* \param constraint The direction or line along which snapping must occur, if 'constrained' is true; otherwise undefined.
* \param uniform true if the transformation should be uniform; only applicable for stretching and scaling.
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
bool constrained,
bool uniform) const
long source_num = 0;
for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) {
Geom::Point transformed = _transformPoint(*i, transformation_type, transformation, origin, dim, uniform);
transformed_points.push_back(Inkscape::SnapCandidatePoint(transformed, (*i).getSourceType(), source_num, Inkscape::SNAPTARGET_UNDEFINED, Geom::OptRect()));
source_num++;
bool first_free_snap = true;
for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) {
Geom::Point const b = ((*i).getPoint() - origin); // vector to original point (not the transformed point! required for rotations!)
if (constrained) {
// PS2: We cannot easily filter these points upstream, e.g. in the grab() method (seltrans.cpp)
// Filtering could be done in handleRequest() (again in seltrans.cpp), by iterating through
// earlier on, e.g. in seltrans.cpp but we're being lazy there and don't want to add an iteration loop)
dedicated_constraint = Inkscape::Snapper::SnapConstraint((*i).getPoint(), constraint.getDirection());
// If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
// requirements, then freeSnap might never see the SnapCandidatePoint with source_num == 0. The freeSnap()
// method in the object snapper depends on this, because only for source-num == 0 the target nodes will
// be collected. Therefore we enforce that the first SnapCandidatePoint that is to be freeSnapped always
// TODO: This is a bit ugly so fix this; do we need sourcenum for anything else? if we don't then get rid
if (first_free_snap) {
(*j).setSourceNum(0);
first_free_snap = false;
switch (transformation_type) {
case TRANSLATE:
case SCALE:
if (fabs(fabs(a[index]/b[index]) - fabs(transformation[index])) > 1e-12) { // if SNAPPING DID occur in this direction
if (uniform) {
Geom::Point scale_metric = Geom::abs(result - transformation); // One or both of its components might be NR_HUGE
case STRETCH:
case SKEW:
case ROTATE:
// a is vector to snapped point; b is vector to original point; now lets calculate angle between a and b
return best_snapped_point;
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \param tr Proposed translation; the final translation can only be calculated after snapping has occurred
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::freeSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
return _snapTransformed(p, pointer, false, Geom::Point(0,0), TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \param tr Proposed translation; the final translation can only be calculated after snapping has occurred.
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::constrainedSnapTranslate(std::vector<Inkscape::SnapCandidatePoint> const &p,
return _snapTransformed(p, pointer, true, constraint, TRANSLATE, tr, Geom::Point(0,0), Geom::X, false);
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::freeSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
Geom::Point pt = _transformPoint(p.at(0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false);
return _snapTransformed(p, pointer, false, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, false);
* \brief Apply a scaling to a set of points and snap such that the aspect ratio of the selection is preserved
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::constrainedSnapScale(std::vector<Inkscape::SnapCandidatePoint> const &p,
Geom::Point pt = _transformPoint(p.at(0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true);
return _snapTransformed(p, pointer, true, Geom::Point(0,0), SCALE, Geom::Point(s[Geom::X], s[Geom::Y]), o, Geom::X, true);
* \brief Apply a stretch to a set of points and snap such that the direction of the stretch is preserved
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::constrainedSnapStretch(std::vector<Inkscape::SnapCandidatePoint> const &p,
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::SnapCandidatePoint> const &p,
// Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
// the transformation of the bounding box is equal to the transformation of the individual nodes. This is
// NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
// so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
if (p.size() > 0) {
* \param p Collection of points to snap (snap sources), at their untransformed position, all points undergoing the same transformation. Paired with an identifier of the type of the snap source.
* \param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
* \param angle Proposed rotation (in radians); the final rotation can only be calculated after snapping has occurred
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
Inkscape::SnappedPoint SnapManager::constrainedSnapRotate(std::vector<Inkscape::SnapCandidatePoint> const &p,
// Snapping the nodes of the bounding box of a selection that is being transformed, will only work if
// the transformation of the bounding box is equal to the transformation of the individual nodes. This is
// NOT the case for example when rotating or skewing. The bounding box itself cannot possibly rotate or skew,
// so it's corners have a different transformation. The snappers cannot handle this, therefore snapping
return _snapTransformed(p, pointer, true, Geom::Point(0,0), ROTATE, Geom::Point(angle, angle), o, Geom::X, false);
* \param constrained True if the snap is constrained, e.g. for stretching or for purely horizontal translation.
* \param noCurves If true, then do consider snapping to intersections of curves, but not to the curves themselves
* \param allowOffScreen If true, then snapping to points which are off the screen is allowed (needed for example when pasting to the grid)
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics
bool constrained,
bool noCurves,
bool allowOffScreen) const
std::cout << " Points : " << sc.points.size() << std::endl;
std::cout << " Lines : " << sc.lines.size() << std::endl;
std::cout << " Grid lines : " << sc.grid_lines.size()<< std::endl;
std::cout << " Guide lines : " << sc.guide_lines.size()<< std::endl;
std::cout << " Curves : " << sc.curves.size()<< std::endl;
if (!noCurves) {
if (getClosestIntersectionCS(sc.curves, p.getPoint(), closestCurvesIntersection, _desktop->dt2doc())) {
// Therefore we will try get fully constrained by finding an intersection with another grid/guide/path
// When doing a constrained snap however, we're already at an intersection of the constrained line and
if (!constrained) {
for (std::list<Inkscape::SnappedPoint>::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) {
// std::cout << "sp = " << (*i).getPoint() << " | source = " << (*i).getSource() << " | target = " << (*i).getTarget();
bestSnappedPoint = *i;
if (_snapindicator) {
// std::cout << "findBestSnap = " << bestSnappedPoint.getPoint() << " | dist = " << bestSnappedPoint.getSnapDistance() << std::endl;
return bestSnappedPoint;
bool snapindicator,
g_warning("The snapmanager has been set up before, but unSetup() hasn't been called afterwards. It possibly held invalid pointers");
* \brief Prepare the snap manager for the actual snapping, which includes building a list of snap targets
* There are two overloaded setup() methods, of which the other one only allows for a single item to be ignored
* \param snapindicator If true then a snap indicator will be displayed automatically (when enabled in the preferences)
* \param items_to_ignore These items will not be snapped to, e.g. the items that are currently being dragged. This avoids "self-snapping"
* \param unselected_nodes Stationary nodes of the path that is currently being edited in the node tool and
* that can be snapped too. Nodes not in this list will not be snapped to, to avoid "self-snapping". Of each
* unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
bool snapindicator,
g_warning("The snapmanager has been set up before, but unSetup() hasn't been called afterwards. It possibly held invalid pointers");
bool snapindicator,
g_warning("The snapmanager has been set up before, but unSetup() hasn't been called afterwards. It possibly held invalid pointers");
* \brief Takes an untransformed point, applies the given transformation, and returns the transformed point. Eliminates lots of duplicated code
* \param p The untransformed position of the point, paired with an identifier of the type of the snap source.
* \param uniform true if the transformation should be uniform; only applicable for stretching and scaling.
bool const uniform) const
switch (transformation_type) {
case TRANSLATE:
case SCALE:
transformed = (p.getPoint() - origin) * Geom::Scale(transformation[Geom::X], transformation[Geom::Y]) + origin;
case STRETCH:
if (uniform)
case SKEW:
transformed[dim] = (p.getPoint())[dim] + transformation[0] * ((p.getPoint())[1 - dim] - origin[1 - dim]);
// While skewing, mirroring and scaling (by integer multiples) in the opposite direction is also allowed.
case ROTATE:
return transformed;
* \brief Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol
* \param point_type Category of points to which the source point belongs: node, guide or bounding box
* \param p The transformed position of the source point, paired with an identifier of the type of the snap source.
if (snapprefs.getSnapEnabledGlobally() && (p_is_other || (p_is_a_node && snapprefs.getSnapModeNode()) || (p_is_a_bbox && snapprefs.getSnapModeBBox()))) {