1 #define __SP_DESKTOP_SNAP_C__
3 /**
4 * \file snap.cpp
5 *
6 * \brief Various snapping methods
7 *
8 * Authors:
9 * Lauris Kaplinski <lauris@kaplinski.com>
10 * Frank Felfe <innerspace@iname.com>
11 * Carl Hetherington <inkscape@carlh.net>
12 *
13 * Copyright (C) 1999-2002 Authors
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 */
18 #include "sp-namedview.h"
19 #include "snap.h"
20 #include <libnr/nr-point-fns.h>
21 #include <libnr/nr-scale-ops.h>
22 #include <libnr/nr-values.h>
24 SnapManager::SnapManager(SPNamedView* v) : grid(v, 0), guide(v, 0), object(v, 0)
25 {
27 }
29 SnapManager::SnapperList SnapManager::getSnappers() const
30 {
31 SnapManager::SnapperList s;
32 s.push_back(&grid);
33 s.push_back(&guide);
34 s.push_back(&object);
35 return s;
36 }
38 /**
39 * \return true if one of the snappers will try to snap something.
40 */
41 bool SnapManager::willSnapSomething() const
42 {
43 SnapperList const s = getSnappers();
44 SnapperList::const_iterator i = s.begin();
45 while (i != s.end() && (*i)->willSnapSomething() == false) {
46 i++;
47 }
49 return (i != s.end());
50 }
53 /* FIXME: lots of cut-and-paste here. This needs some
54 ** functor voodoo to cut it all down a bit.
55 */
57 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
58 NR::Point const &p,
59 SPItem const *it) const
61 {
62 std::list<SPItem const *> lit;
63 lit.push_back(it);
64 return freeSnap(t, p, lit);
65 }
68 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
69 NR::Point const &p,
70 std::list<SPItem const *> const &it) const
71 {
72 Inkscape::SnappedPoint r(p, NR_HUGE);
74 SnapperList const snappers = getSnappers();
75 for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
76 Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
77 if (s.getDistance() < r.getDistance()) {
78 r = s;
79 }
80 }
82 return r;
83 }
86 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
87 NR::Point const &p,
88 NR::Point const &c,
89 SPItem const *it) const
90 {
91 std::list<SPItem const *> lit;
92 lit.push_back(it);
93 return constrainedSnap(t, p, c, lit);
94 }
97 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
98 NR::Point const &p,
99 NR::Point const &c,
100 std::list<SPItem const *> const &it) const
101 {
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)->constrainedSnap(t, p, c, it);
107 if (s.getDistance() < r.getDistance()) {
108 r = s;
109 }
110 }
112 return r;
113 }
116 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
117 std::vector<NR::Point> const &p,
118 std::list<SPItem const *> const &it,
119 NR::Point const &tr) const
120 {
121 if (willSnapSomething() == false) {
122 return std::make_pair(tr, false);
123 }
125 NR::Point best_translation = tr;
126 NR::Coord best_distance = NR_HUGE;
128 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
129 /* Translated version of this point */
130 NR::Point const q = *i + tr;
131 /* Snap it */
132 Inkscape::SnappedPoint s = freeSnap(t, q, it);
133 if (s.getDistance() < NR_HUGE) {
134 /* Resulting translation */
135 NR::Point const r = s.getPoint() - *i;
136 NR::Coord const d = NR::L2(r);
137 if (d < best_distance) {
138 best_distance = d;
139 best_translation = r;
140 }
141 }
142 }
144 return std::make_pair(best_translation, best_distance < NR_HUGE);
145 }
149 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
150 std::vector<NR::Point> const &p,
151 NR::Point const &c,
152 std::list<SPItem const *> const &it,
153 NR::Point const &tr) const
154 {
155 if (willSnapSomething() == false) {
156 return std::make_pair(tr, false);
157 }
159 NR::Point best_translation = tr;
160 NR::Coord best_distance = NR_HUGE;
162 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
163 /* Translated version of this point */
164 NR::Point const q = *i + tr;
165 /* Snap it */
166 Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it);
167 if (s.getDistance() < NR_HUGE) {
168 /* Resulting translation */
169 NR::Point const r = s.getPoint() - *i;
170 NR::Coord const d = NR::L2(r);
171 if (d < best_distance) {
172 best_distance = d;
173 best_translation = r;
174 }
175 }
176 }
178 return std::make_pair(best_translation, best_distance < NR_HUGE);
179 }
186 /// Minimal distance to norm before point is considered for snap.
187 static const double MIN_DIST_NORM = 1.0;
189 /**
190 * Try to snap \a req in one dimension.
191 *
192 * \param nv NamedView to use.
193 * \param req Point to snap; updated to the snapped point if a snap occurred.
194 * \param dim Dimension to snap in.
195 * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE
196 * if no snap occurred.
197 */
198 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
199 NR::Dim2 const dim, SPItem const *it)
200 {
201 return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
202 }
204 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
205 NR::Dim2 const dim, std::list<SPItem const *> const &it)
206 {
207 return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
208 }
211 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
212 NR::Point &req, NR::Point const &d,
213 SPItem const *it)
214 {
215 std::list<SPItem const *> lit;
216 lit.push_back(it);
217 return namedview_vector_snap(nv, t, req, d, lit);
218 }
220 /**
221 * Look for snap point along the line described by the point \a req
222 * and the direction vector \a d.
223 * Modifies req to the snap point, if one is found.
224 * \return The distance from \a req to the snap point along the vector \a d,
225 * or \c NR_HUGE if no snap point was found.
226 *
228 */
229 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
230 NR::Point &req, NR::Point const &d,
231 std::list<SPItem const *> const &it)
232 {
233 g_assert(nv != NULL);
234 g_assert(SP_IS_NAMEDVIEW(nv));
236 SnapManager::SnapperList const snappers = nv->snap_manager.getSnappers();
238 NR::Coord best = NR_HUGE;
239 for (SnapManager::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
240 Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it);
241 if (s.getDistance() < best) {
242 req = s.getPoint();
243 best = s.getDistance();
244 }
245 }
247 return best;
248 }
251 /*
252 * functions for lists of points
253 *
254 * All functions take a list of NR::Point and parameter indicating the proposed transformation.
255 * They return the updated transformation parameter.
256 */
258 /**
259 * Snap list of points in two dimensions.
260 */
261 std::pair<double, bool> namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
262 const std::vector<NR::Point> &p, NR::Point const &norm,
263 NR::scale const &s, std::list<SPItem const *> const &it)
264 {
265 using NR::X;
266 using NR::Y;
268 SnapManager const &m = nv->snap_manager;
270 if (m.willSnapSomething() == false) {
271 return std::make_pair(s[X], false);
272 }
274 NR::Coord dist = NR_HUGE;
275 double ratio = fabs(s[X]);
276 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
277 NR::Point const &q = *i;
278 NR::Point check = ( q - norm ) * s + norm;
279 if (NR::LInfty( q - norm ) > MIN_DIST_NORM) {
280 NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it);
281 if (d < dist) {
282 dist = d;
283 NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] ) >
284 fabs( q[Y] - norm[Y] ) )
285 ? X
286 : Y );
287 ratio = ( ( check[dominant] - norm[dominant] )
288 / ( q[dominant] - norm[dominant] ) );
289 }
290 }
291 }
293 return std::make_pair(ratio, dist < NR_HUGE);
294 }
297 /**
298 * Try to snap points in \a p after they have been scaled by \a sx with respect to
299 * the origin \a norm. The best snap is the one that changes the scale least.
300 *
301 * \return Pair containing snapped scale and a flag which is true if a snap was made.
302 */
303 std::pair<double, bool> namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t,
304 const std::vector<NR::Point> &p, NR::Point const &norm,
305 double const sx, NR::Dim2 dim,
306 std::list<const SPItem *> const &it)
307 {
308 SnapManager const &m = nv->snap_manager;
309 if (m.willSnapSomething() == false) {
310 return std::make_pair(sx, false);
311 }
313 g_assert(dim < 2);
315 NR::Coord dist = NR_HUGE;
316 double scale = sx;
318 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
319 NR::Point q = *i;
320 NR::Point check = q;
322 /* Scaled version of the point we are looking at */
323 check[dim] = (sx * (q - norm) + norm)[dim];
325 if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) {
326 /* Snap this point */
327 const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it);
328 /* Work out the resulting scale factor */
329 double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]);
331 if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) {
332 /* This is either the first point, or the snapped scale
333 ** is the closest yet to the original.
334 */
335 scale = snapped_scale;
336 dist = d;
337 }
338 }
339 }
341 return std::make_pair(scale, dist < NR_HUGE);
342 }
344 /**
345 * Try to snap points after they have been skewed.
346 */
347 double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t,
348 const std::vector<NR::Point> &p, NR::Point const &norm,
349 double const sx, NR::Dim2 const dim)
350 {
351 SnapManager const &m = nv->snap_manager;
353 if (m.willSnapSomething() == false) {
354 return sx;
355 }
357 g_assert(dim < 2);
359 gdouble dist = NR_HUGE;
360 gdouble skew = sx;
362 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
363 NR::Point q = *i;
364 NR::Point check = q;
365 // apply shear
366 check[dim] += sx * (q[!dim] - norm[!dim]);
367 if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) {
368 const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL);
369 if (d < fabs (dist)) {
370 dist = d;
371 skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]);
372 }
373 }
374 }
376 return skew;
377 }
380 /*
381 Local Variables:
382 mode:c++
383 c-file-style:"stroustrup"
384 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
385 indent-tabs-mode:nil
386 fill-column:99
387 End:
388 */
389 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :