44
/// Class to coordinate snapping operations
44
* Class to coordinate snapping operations.
46
* The SnapManager class handles most (if not all) of the interfacing of the snapping mechanisms
47
* with the other parts of the code base. It stores the references to the various types of snappers
48
* for grid, guides and objects, and it stores most of the snapping preferences. Besides that
49
* it provides methods to setup the snapping environment (e.g. keeps a list of the items to ignore
50
* when looking for snap target candidates, and toggling of the snap indicator), and it provides
51
* many different methods for snapping queries (free snapping vs. constrained snapping,
52
* returning the result by reference or through a return statement, etc.)
54
* Each SPNamedView has one of these. It offers methods to snap points to whatever
55
* snappers are defined (e.g. grid, guides etc.). It also allows callers to snap
56
* points which have undergone some transformation (e.g. translation, scaling etc.)
58
* \par How snapping is implemented in Inkscape
60
* The snapping system consists of two key elements. The first one is the snap manager
61
* (this class), which keeps some data about objects in the document and answers queries
62
* of the type "given this point and type of transformation, what is the best place
65
* The second is in event-context.cpp and implements the snapping timeout. Whenever a motion
66
* events happens over the canvas, it stores it for later use and initiates a timeout.
67
* This timeout is discarded whenever a new motion event occurs. When the timeout expires,
68
* a global flag in SnapManager, accessed via getSnapPostponedGlobally(), is set to true
69
* and the stored event is replayed, but this time with snapping enabled. This way you can
70
* write snapping code directly in your control point's dragged handler as if there was
47
* Each SPNamedView has one of these. It offers methods to snap points to whatever
48
* snappers are defined (e.g. grid, guides etc.). It also allows callers to snap
49
* points which have undergone some transformation (e.g. translation, scaling etc.)
85
* Construct a SnapManager for a SPNamedView.
87
* @param v 'Owning' SPNamedView.
89
SnapManager(SPNamedView const *v);
62
SnapManager(SPNamedView const *v);
91
64
typedef std::list<const Inkscape::Snapper*> SnapperList;
94
* Return true if any snapping might occur, whether its to grids, guides or objects.
96
* Each snapper instance handles its own snapping target, e.g. grids, guides or
97
* objects. This method iterates through all these snapper instances and returns
98
* true if any of the snappers might possible snap, considering only the relevant
99
* snapping preferences.
101
* @return true if one of the snappers will try to snap to something.
103
bool someSnapperMightSnap(bool immediately = true) const;
106
* @return true if one of the grids might be snapped to.
66
bool someSnapperMightSnap() const;
108
67
bool gridSnapperMightSnap() const;
111
* Convenience shortcut when there is only one item to ignore.
113
void setup(SPDesktop const *desktop,
114
bool snapindicator = true,
115
SPItem const *item_to_ignore = NULL,
116
std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL,
117
SPGuide *guide_to_ignore = NULL);
120
* Prepare the snap manager for the actual snapping, which includes building a list of snap targets
121
* to ignore and toggling the snap indicator.
123
* There are two overloaded setup() methods, of which the other one only allows for a single item to be ignored
124
* whereas this one will take a list of items to ignore
126
* @param desktop Reference to the desktop to which this snap manager is attached.
127
* @param snapindicator If true then a snap indicator will be displayed automatically (when enabled in the preferences).
128
* @param items_to_ignore These items will not be snapped to, e.g. the items that are currently being dragged. This avoids "self-snapping".
129
* @param unselected_nodes Stationary nodes of the path that is currently being edited in the node tool and
130
* that can be snapped too. Nodes not in this list will not be snapped to, to avoid "self-snapping". Of each
131
* unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored.
132
* @param guide_to_ignore Guide that is currently being dragged and should not be snapped to.
134
void setup(SPDesktop const *desktop,
136
std::vector<SPItem const *> &items_to_ignore,
137
std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL,
138
SPGuide *guide_to_ignore = NULL);
140
void setupIgnoreSelection(SPDesktop const *desktop,
141
bool snapindicator = true,
142
std::vector<Inkscape::SnapCandidatePoint> *unselected_nodes = NULL,
143
SPGuide *guide_to_ignore = NULL);
145
void unSetup() {_rotation_center_source_items.clear();
146
_guide_to_ignore = NULL;
148
_unselected_nodes = NULL;}
150
// If we're dragging a rotation center, then setRotationCenterSource() stores the parent item
151
// of this rotation center; this reference is used to make sure that we do not snap a rotation
153
// NOTE: Must be called after calling setup(), not before!
154
void setRotationCenterSource(const std::vector<SPItem*> &items) {_rotation_center_source_items = items;}
155
const std::vector<SPItem*> &getRotationCenterSource() {return _rotation_center_source_items;}
69
void setup(SPDesktop const *desktop,
70
bool snapindicator = true,
71
SPItem const *item_to_ignore = NULL,
72
std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL,
73
SPGuide *guide_to_ignore = NULL);
75
void setup(SPDesktop const *desktop,
77
std::vector<SPItem const *> &items_to_ignore,
78
std::vector<std::pair<Geom::Point, int> > *unselected_nodes = NULL,
79
SPGuide *guide_to_ignore = NULL);
157
81
// freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a
158
82
// point if snapping has occurred (by overwriting p); otherwise p is untouched
161
* Try to snap a point to grids, guides or objects.
163
* Try to snap a point to grids, guides or objects, in two degrees-of-freedom,
164
* i.e. snap in any direction on the two dimensional canvas to the nearest
165
* snap target. freeSnapReturnByRef() is equal in snapping behavior to
166
* freeSnap(), but the former returns the snapped point trough the referenced
167
* parameter p. This parameter p initially contains the position of the snap
168
* source and will we overwritten by the target position if snapping has occurred.
169
* This makes snapping transparent to the calling code. If this is not desired
170
* because either the calling code must know whether snapping has occurred, or
171
* because the original position should not be touched, then freeSnap() should be
175
* 1) SnapManager::setup() must have been called before calling this method,
176
* although only once for each set of points
177
* 2) Only to be used when a single source point is to be snapped; it assumes
178
* that source_num = 0, which is inefficient when snapping sets our source points
180
* @param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred.
181
* @param source_type Detailed description of the source type, will be used by the snap indicator.
182
* @param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation.
184
void freeSnapReturnByRef(Geom::Point &p,
185
Inkscape::SnapSourceType const source_type,
186
Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
189
* Try to snap a point to grids, guides or objects.
191
* Try to snap a point to grids, guides or objects, in two degrees-of-freedom,
192
* i.e. snap in any direction on the two dimensional canvas to the nearest
193
* snap target. freeSnap() is equal in snapping behavior to
194
* freeSnapReturnByRef(). Please read the comments of the latter for more details
196
* PS: SnapManager::setup() must have been called before calling this method,
197
* although only once for each set of points
199
* @param p Source point to be snapped.
200
* @param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation.
201
* @param to_path_only Only snap to points on a path, such as path intersections with itself or with grids/guides. This is used for
202
* example when adding nodes to a path. We will not snap for example to grid intersections
203
* @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
205
Inkscape::SnappedPoint freeSnap(Inkscape::SnapCandidatePoint const &p,
206
Geom::OptRect const &bbox_to_snap = Geom::OptRect(),
207
bool to_path_only = false) const;
209
void preSnap(Inkscape::SnapCandidatePoint const &p, bool to_path_only = false);
212
* Snap to the closest multiple of a grid pitch.
214
* When pasting, we would like to snap to the grid. Problem is that we don't know which
215
* nodes were aligned to the grid at the time of copying, so we don't know which nodes
216
* to snap. If we'd snap an unaligned node to the grid, previously aligned nodes would
217
* become unaligned. That's undesirable. Instead we will make sure that the offset
218
* between the source and its pasted copy is a multiple of the grid pitch. If the source
219
* was aligned, then the copy will therefore also be aligned.
221
* PS: Whether we really find a multiple also depends on the snapping range! Most users
222
* will have "always snap" enabled though, in which case a multiple will always be found.
223
* PS2: When multiple grids are present then the result will become ambiguous. There is no
224
* way to control to which grid this method will snap.
226
* @param t Vector that represents the offset of the pasted copy with respect to the original.
227
* @return Offset vector after snapping to the closest multiple of a grid pitch.
229
Geom::Point multipleOfGridPitch(Geom::Point const &t, Geom::Point const &origin);
83
void freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type,
85
Inkscape::SnapSourceType const source_type,
86
bool first_point = true,
87
Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
90
Inkscape::SnappedPoint freeSnap(Inkscape::SnapPreferences::PointType point_type,
92
Inkscape::SnapSourceType const &source_type,
93
bool first_point = true,
94
Geom::OptRect const &bbox_to_snap = Geom::OptRect() ) const;
96
Geom::Point multipleOfGridPitch(Geom::Point const &t) const;
231
98
// constrainedSnapReturnByRef() is preferred over constrainedSnap(), because it only returns a
232
99
// point, by overwriting p, if snapping has occurred; otherwise p is untouched
235
* Try to snap a point along a constraint line to grids, guides or objects.
237
* Try to snap a point to grids, guides or objects, in only one degree-of-freedom,
238
* i.e. snap in a specific direction on the two dimensional canvas to the nearest
241
* constrainedSnapReturnByRef() is equal in snapping behavior to
242
* constrainedSnap(), but the former returns the snapped point trough the referenced
243
* parameter p. This parameter p initially contains the position of the snap
244
* source and will be overwritten by the target position if snapping has occurred.
245
* This makes snapping transparent to the calling code. If this is not desired
246
* because either the calling code must know whether snapping has occurred, or
247
* because the original position should not be touched, then constrainedSnap() should
248
* be called instead. If there's nothing to snap to or if snapping has been disabled,
249
* then this method will still apply the constraint (but without snapping)
252
* 1) SnapManager::setup() must have been called before calling this method,
253
* although only once for each set of points
254
* 2) Only to be used when a single source point is to be snapped; it assumes
255
* that source_num = 0, which is inefficient when snapping sets our source points
258
* @param p Current position of the snap source; will be overwritten by the position of the snap target if snapping has occurred.
259
* @param source_type Detailed description of the source type, will be used by the snap indicator.
260
* @param constraint The direction or line along which snapping must occur.
261
* @param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation.
263
void constrainedSnapReturnByRef(Geom::Point &p,
264
Inkscape::SnapSourceType const source_type,
265
Inkscape::Snapper::SnapConstraint const &constraint,
266
Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
269
* Try to snap a point along a constraint line to grids, guides or objects.
271
* Try to snap a point to grids, guides or objects, in only one degree-of-freedom,
272
* i.e. snap in a specific direction on the two dimensional canvas to the nearest
273
* snap target. constrainedSnap is equal in snapping behavior to
274
* constrainedSnapReturnByRef(). Please read the comments of the latter for more details.
276
* PS: SnapManager::setup() must have been called before calling this method,
277
* although only once for each set of points
278
* PS: If there's nothing to snap to or if snapping has been disabled, then this
279
* method will still apply the constraint (but without snapping)
281
* @param p Source point to be snapped.
282
* @param constraint The direction or line along which snapping must occur.
283
* @param bbox_to_snap Bounding box hulling the set of points, all from the same selection and having the same transformation.
285
Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapCandidatePoint const &p,
286
Inkscape::Snapper::SnapConstraint const &constraint,
100
void constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type,
102
Inkscape::SnapSourceType const source_type,
103
Inkscape::Snapper::ConstraintLine const &constraint,
104
bool first_point = true,
105
Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
107
Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapPreferences::PointType point_type,
108
Geom::Point const &p,
109
Inkscape::SnapSourceType const &source_type,
110
Inkscape::Snapper::ConstraintLine const &constraint,
111
bool first_point = true,
287
112
Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
289
Inkscape::SnappedPoint multipleConstrainedSnaps(Inkscape::SnapCandidatePoint const &p,
290
std::vector<Inkscape::Snapper::SnapConstraint> const &constraints,
291
bool dont_snap = false,
292
Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const;
295
* Try to snap a point to something at a specific angle.
297
* When drawing a straight line or modifying a gradient, it will snap to specific angle increments
298
* if CTRL is being pressed. This method will enforce this angular constraint (even if there is nothing
301
* @param p Source point to be snapped.
302
* @param p_ref Optional original point, relative to which the angle should be calculated. If empty then
303
* the angle will be calculated relative to the y-axis.
304
* @param snaps Number of angular increments per PI radians; E.g. if snaps = 2 then we will snap every PI/2 = 90 degrees.
306
Inkscape::SnappedPoint constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p,
307
boost::optional<Geom::Point> const &p_ref,
308
Geom::Point const &o,
309
unsigned const snaps) const;
312
* Wrapper method to make snapping of the guide origin a bit easier (i.e. simplifies the calling code).
314
* PS: SnapManager::setup() must have been called before calling this method,
316
* @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.
317
* @param origin_or_vector Data used for tangential and perpendicular snapping. When rotating a guide the origin of the rotation is specified here, whereas when
318
* dragging a guide its vector is specified here
319
* @param origin If true then origin_or_vector contains an origin, other it contains a vector
320
* @param freeze_angle If true (in which case origin is false), then the vector specified in origin_or_vector will not be touched, i.e. the guide will
321
* in all circumstances keep its angle. Otherwise the vector in origin_or_vector can be updated, meaning that the guide might take on the angle of a curve that
322
* has been snapped too tangentially or perpendicularly
324
void guideFreeSnap(Geom::Point &p, Geom::Point &origin_or_vector, bool origin, bool freeze_angle) const;
327
* Wrapper method to make snapping of the guide origin a bit easier (i.e. simplifies the calling code).
329
* PS: SnapManager::setup() must have been called before calling this method,
331
* @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.
332
* @param guideline The guide that is currently being dragged
114
void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const;
334
115
void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const;
117
Inkscape::SnappedPoint freeSnapTranslation(Inkscape::SnapPreferences::PointType point_type,
118
std::vector<std::pair<Geom::Point, int> > const &p,
119
Geom::Point const &pointer,
120
Geom::Point const &tr) const;
122
Inkscape::SnappedPoint constrainedSnapTranslation(Inkscape::SnapPreferences::PointType point_type,
123
std::vector<std::pair<Geom::Point, int> > const &p,
124
Geom::Point const &pointer,
125
Inkscape::Snapper::ConstraintLine const &constraint,
126
Geom::Point const &tr) const;
128
Inkscape::SnappedPoint freeSnapScale(Inkscape::SnapPreferences::PointType point_type,
129
std::vector<std::pair<Geom::Point, int> > const &p,
130
Geom::Point const &pointer,
131
Geom::Scale const &s,
132
Geom::Point const &o) const;
134
Inkscape::SnappedPoint constrainedSnapScale(Inkscape::SnapPreferences::PointType point_type,
135
std::vector<std::pair<Geom::Point, int> > const &p,
136
Geom::Point const &pointer,
137
Geom::Scale const &s,
138
Geom::Point const &o) const;
140
Inkscape::SnappedPoint constrainedSnapStretch(Inkscape::SnapPreferences::PointType point_type,
141
std::vector<std::pair<Geom::Point, int> > const &p,
142
Geom::Point const &pointer,
143
Geom::Coord const &s,
144
Geom::Point const &o,
148
Inkscape::SnappedPoint constrainedSnapSkew(Inkscape::SnapPreferences::PointType point_type,
149
std::vector<std::pair<Geom::Point, int> > const &p,
150
Geom::Point const &pointer,
151
Inkscape::Snapper::ConstraintLine const &constraint,
152
Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor
153
Geom::Point const &o,
336
156
Inkscape::GuideSnapper guide; ///< guide snapper
337
157
Inkscape::ObjectSnapper object; ///< snapper to other objects
338
158
Inkscape::SnapPreferences snapprefs;
341
* Return a list of snappers.
343
* Inkscape snaps to objects, grids, and guides. For each of these snap targets a
344
* separate class is used, which has been derived from the base Snapper class. The
345
* getSnappers() method returns a list of pointers to instances of this class. This
346
* list contains exactly one instance of the guide snapper and of the object snapper
347
* class, but any number of grid snappers (because each grid has its own snapper
350
* @return List of snappers that we use.
352
160
SnapperList getSnappers() const;
355
* Return a list of gridsnappers.
357
* Each grid has its own instance of the snapper class. This way snapping can
358
* be enabled per grid individually. A list will be returned containing the
359
* pointers to these instances, but only for grids that are being displayed
360
* and for which snapping is enabled.
362
* @return List of gridsnappers that we use.
364
161
SnapperList getGridSnappers() const;
366
163
SPDesktop const *getDesktop() const {return _desktop;}
371
168
bool getSnapIndicator() const {return _snapindicator;}
374
* Given a set of possible snap targets, find the best target (which is not necessarily
375
* also the nearest target), and show the snap indicator if requested.
377
* @param p Source point to be snapped.
378
* @param isr A structure holding all snap targets that have been found so far.
379
* @param constrained True if the snap is constrained, e.g. for stretching or for purely horizontal translation.
380
* @param allowOffScreen If true, then snapping to points which are off the screen is allowed (needed for example when pasting to the grid).
381
* @param to_path_only Only snap to points on a path, such as path intersections with itself or with grids/guides. This is used for
382
* example when adding nodes to a path. We will not snap for example to grid intersections
383
* @return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics.
385
Inkscape::SnappedPoint findBestSnap(Inkscape::SnapCandidatePoint const &p, IntermSnapResults const &isr, bool constrained, bool allowOffScreen = false, bool to_paths_only = false) const;
388
* Mark the location of the snap source (not the snap target!) on the canvas by drawing a symbol.
390
* @param point_type Category of points to which the source point belongs: node, guide or bounding box.
391
* @param p The transformed position of the source point, paired with an identifier of the type of the snap source.
393
void displaySnapsource(Inkscape::SnapCandidatePoint const &p) const;
396
* Method for snapping sets of points while they are being transformed.
398
* Method for snapping sets of points while they are being transformed, when using
399
* for example the selector tool. This method is for internal use only, and should
400
* not have to be called directly. Use freeSnapTransalation(), constrainedSnapScale(),
403
* This is what is being done in this method: transform each point, find out whether
404
* a free snap or constrained snap is more appropriate, do the snapping, calculate
405
* some metrics to quantify the snap "distance", and see if it's better than the
406
* previous snap. Finally, the best ("nearest") snap from all these points is returned.
407
* If no snap has occurred and we're asked for a constrained snap then the constraint
408
* will be applied nevertheless
410
* @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.
411
* @param pointer Location of the mouse pointer at the time dragging started (i.e. when the selection was still untransformed).
412
* @param transform Describes the type of transformation, it's parameters, and any additional constraints
414
void snapTransformed(std::vector<Inkscape::SnapCandidatePoint> const &points,
415
Geom::Point const &pointer,
416
Inkscape::PureTransform &transform);
419
171
SPNamedView const *_named_view;
422
std::vector<SPItem const *> _items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method
423
std::vector<SPItem*> _rotation_center_source_items; // to avoid snapping a rotation center to itself
174
std::vector<SPItem const *> *_items_to_ignore; ///< Items that should not be snapped to, for example the items that are currently being dragged. Set using the setup() method
175
SPItem const *_item_to_ignore; ///< Single item that should not be snapped to. If not NULL then this takes precedence over _items_to_ignore. Set using the setup() method
424
176
SPGuide *_guide_to_ignore; ///< A guide that should not be snapped to, e.g. the guide that is currently being dragged
425
177
SPDesktop const *_desktop;
426
178
bool _snapindicator; ///< When true, an indicator will be drawn at the position that was being snapped to
427
std::vector<Inkscape::SnapCandidatePoint> *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
179
std::vector<std::pair<Geom::Point, int> > *_unselected_nodes; ///< Nodes of the path that is currently being edited and which have not been selected and which will therefore be stationary. Only these nodes will be considered for snapping to. Of each unselected node both the position (Geom::Point) and the type (Inkscape::SnapTargetType) will be stored
180
//TODO: Make _unselected_nodes type safe; in the line above int is used for Inkscape::SnapTargetType, but if I remember
181
//correctly then in other cases the int is being used for Inkscape::SnapSourceType, or for both. How to make
184
Inkscape::SnappedPoint _snapTransformed(Inkscape::SnapPreferences::PointType type,
185
std::vector<std::pair<Geom::Point, int> > const &points,
186
Geom::Point const &pointer,
188
Inkscape::Snapper::ConstraintLine const &constraint,
189
Transformation transformation_type,
190
Geom::Point const &transformation,
191
Geom::Point const &origin,
195
Geom::Point _transformPoint(std::pair<Geom::Point, int> const &p,
196
Transformation const transformation_type,
197
Geom::Point const &transformation,
198
Geom::Point const &origin,
199
Geom::Dim2 const dim,
200
bool const uniform) const;
202
void _displaySnapsource(Inkscape::SnapPreferences::PointType point_type, std::pair<Geom::Point, int> const &p) const;
204
Inkscape::SnappedPoint findBestSnap(Geom::Point const &p, Inkscape::SnapSourceType const source_type, SnappedConstraints &sc, bool constrained, bool noCurves = false) const;
431
#endif // !SEEN_SNAP_H
207
#endif /* !SEEN_SNAP_H */