940d4c77208c6f17f898423baf692080a67892d0
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::SnapperList SnapManager::getSnappers() const
25 {
26 SnapManager::SnapperList s;
27 s.push_back(&grid);
28 s.push_back(&guide);
29 s.push_back(&object);
30 return s;
31 }
33 /**
34 * \return true if one of the snappers will try to snap something.
35 */
36 bool SnapManager::willSnapSomething() const
37 {
38 SnapperList const s = getSnappers();
39 SnapperList::const_iterator i = s.begin();
40 while (i != s.end() && (*i)->willSnapSomething() == false) {
41 i++;
42 }
44 return (i != s.end());
45 }
48 /* FIXME: lots of cut-and-paste here. This needs some
49 ** functor voodoo to cut it all down a bit.
50 */
52 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
53 NR::Point const &p,
54 SPItem const *it) const
56 {
57 std::list<SPItem const *> lit;
58 lit.push_back(it);
59 return freeSnap(t, p, lit);
60 }
63 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
64 NR::Point const &p,
65 std::list<SPItem const *> const &it) const
66 {
67 Inkscape::SnappedPoint r(p, NR_HUGE);
69 SnapperList const snappers = getSnappers();
70 for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
71 Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
72 if (s.getDistance() < r.getDistance()) {
73 r = s;
74 }
75 }
77 return r;
78 }
81 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
82 NR::Point const &p,
83 NR::Point const &c,
84 SPItem const *it) const
85 {
86 std::list<SPItem const *> lit;
87 lit.push_back(it);
88 return constrainedSnap(t, p, c, lit);
89 }
92 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
93 NR::Point const &p,
94 NR::Point const &c,
95 std::list<SPItem const *> const &it) const
96 {
97 Inkscape::SnappedPoint r(p, NR_HUGE);
99 SnapperList const snappers = getSnappers();
100 for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
101 Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
102 if (s.getDistance() < r.getDistance()) {
103 r = s;
104 }
105 }
107 return r;
108 }
111 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
112 std::vector<NR::Point> const &p,
113 std::list<SPItem const *> const &it,
114 NR::Point const &tr) const
115 {
116 if (willSnapSomething() == false) {
117 return std::make_pair(tr, false);
118 }
120 NR::Point best_translation = tr;
121 NR::Coord best_distance = NR_HUGE;
123 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
124 /* Translated version of this point */
125 NR::Point const q = *i + tr;
126 /* Snap it */
127 Inkscape::SnappedPoint s = freeSnap(t, q, it);
128 if (s.getDistance() < NR_HUGE) {
129 /* Resulting translation */
130 NR::Point const r = s.getPoint() - *i;
131 NR::Coord const d = NR::L2(r);
132 if (d < best_distance) {
133 best_distance = d;
134 best_translation = r;
135 }
136 }
137 }
139 return std::make_pair(best_translation, best_distance < NR_HUGE);
140 }
144 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
145 std::vector<NR::Point> const &p,
146 NR::Point const &c,
147 std::list<SPItem const *> const &it,
148 NR::Point const &tr) const
149 {
150 if (willSnapSomething() == false) {
151 return std::make_pair(tr, false);
152 }
154 NR::Point best_translation = tr;
155 NR::Coord best_distance = NR_HUGE;
157 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
158 /* Translated version of this point */
159 NR::Point const q = *i + tr;
160 /* Snap it */
161 Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it);
162 if (s.getDistance() < NR_HUGE) {
163 /* Resulting translation */
164 NR::Point const r = s.getPoint() - *i;
165 NR::Coord const d = NR::L2(r);
166 if (d < best_distance) {
167 best_distance = d;
168 best_translation = r;
169 }
170 }
171 }
173 return std::make_pair(best_translation, best_distance < NR_HUGE);
174 }
181 /// Minimal distance to norm before point is considered for snap.
182 static const double MIN_DIST_NORM = 1.0;
184 /**
185 * Try to snap \a req in one dimension.
186 *
187 * \param nv NamedView to use.
188 * \param req Point to snap; updated to the snapped point if a snap occurred.
189 * \param dim Dimension to snap in.
190 * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE
191 * if no snap occurred.
192 */
193 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
194 NR::Dim2 const dim, SPItem const *it)
195 {
196 return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
197 }
199 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
200 NR::Dim2 const dim, std::list<SPItem const *> const &it)
201 {
202 return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
203 }
206 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
207 NR::Point &req, NR::Point const &d,
208 SPItem const *it)
209 {
210 std::list<SPItem const *> lit;
211 lit.push_back(it);
212 return namedview_vector_snap(nv, t, req, d, lit);
213 }
215 /**
216 * Look for snap point along the line described by the point \a req
217 * and the direction vector \a d.
218 * Modifies req to the snap point, if one is found.
219 * \return The distance from \a req to the snap point along the vector \a d,
220 * or \c NR_HUGE if no snap point was found.
221 *
223 */
224 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
225 NR::Point &req, NR::Point const &d,
226 std::list<SPItem const *> const &it)
227 {
228 g_assert(nv != NULL);
229 g_assert(SP_IS_NAMEDVIEW(nv));
231 SnapManager::SnapperList const snappers = nv->snap_manager.getSnappers();
233 NR::Coord best = NR_HUGE;
234 for (SnapManager::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
235 Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it);
236 if (s.getDistance() < best) {
237 req = s.getPoint();
238 best = s.getDistance();
239 }
240 }
242 return best;
243 }
246 /*
247 * functions for lists of points
248 *
249 * All functions take a list of NR::Point and parameter indicating the proposed transformation.
250 * They return the updated transformation parameter.
251 */
253 /**
254 * Snap list of points in two dimensions.
255 */
256 std::pair<double, bool> namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
257 const std::vector<NR::Point> &p, NR::Point const &norm,
258 NR::scale const &s, std::list<SPItem const *> const &it)
259 {
260 using NR::X;
261 using NR::Y;
263 SnapManager const &m = nv->snap_manager;
265 if (m.willSnapSomething() == false) {
266 return std::make_pair(s[X], false);
267 }
269 NR::Coord dist = NR_HUGE;
270 double ratio = fabs(s[X]);
271 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
272 NR::Point const &q = *i;
273 NR::Point check = ( q - norm ) * s + norm;
274 if (NR::LInfty( q - norm ) > MIN_DIST_NORM) {
275 NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it);
276 if (d < dist) {
277 dist = d;
278 NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] ) >
279 fabs( q[Y] - norm[Y] ) )
280 ? X
281 : Y );
282 ratio = ( ( check[dominant] - norm[dominant] )
283 / ( q[dominant] - norm[dominant] ) );
284 }
285 }
286 }
288 return std::make_pair(ratio, dist < NR_HUGE);
289 }
292 /**
293 * Try to snap points in \a p after they have been scaled by \a sx with respect to
294 * the origin \a norm. The best snap is the one that changes the scale least.
295 *
296 * \return Pair containing snapped scale and a flag which is true if a snap was made.
297 */
298 std::pair<double, bool> namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t,
299 const std::vector<NR::Point> &p, NR::Point const &norm,
300 double const sx, NR::Dim2 dim,
301 std::list<const SPItem *> const &it)
302 {
303 SnapManager const &m = nv->snap_manager;
304 if (m.willSnapSomething() == false) {
305 return std::make_pair(sx, false);
306 }
308 g_assert(dim < 2);
310 NR::Coord dist = NR_HUGE;
311 double scale = sx;
313 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
314 NR::Point q = *i;
315 NR::Point check = q;
317 /* Scaled version of the point we are looking at */
318 check[dim] = (sx * (q - norm) + norm)[dim];
320 if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) {
321 /* Snap this point */
322 const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it);
323 /* Work out the resulting scale factor */
324 double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]);
326 if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) {
327 /* This is either the first point, or the snapped scale
328 ** is the closest yet to the original.
329 */
330 scale = snapped_scale;
331 dist = d;
332 }
333 }
334 }
336 return std::make_pair(scale, dist < NR_HUGE);
337 }
339 /**
340 * Try to snap points after they have been skewed.
341 */
342 double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t,
343 const std::vector<NR::Point> &p, NR::Point const &norm,
344 double const sx, NR::Dim2 const dim)
345 {
346 SnapManager const &m = nv->snap_manager;
348 if (m.willSnapSomething() == false) {
349 return sx;
350 }
352 g_assert(dim < 2);
354 gdouble dist = NR_HUGE;
355 gdouble skew = sx;
357 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
358 NR::Point q = *i;
359 NR::Point check = q;
360 // apply shear
361 check[dim] += sx * (q[!dim] - norm[!dim]);
362 if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) {
363 const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL);
364 if (d < fabs (dist)) {
365 dist = d;
366 skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]);
367 }
368 }
369 }
371 return skew;
372 }
375 /*
376 Local Variables:
377 mode:c++
378 c-file-style:"stroustrup"
379 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
380 indent-tabs-mode:nil
381 fill-column:99
382 End:
383 */
384 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :