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>
25 /**
26 * \return true if one of the snappers will try to snap something.
27 */
28 bool SnapManager::willSnapSomething() const
29 {
30 SPNamedView::SnapperList s = namedview->getSnappers();
31 SPNamedView::SnapperList::const_iterator i = s.begin();
32 while (i != s.end() && (*i)->willSnapSomething() == false) {
33 i++;
34 }
36 return (i != s.end());
37 }
40 /* FIXME: lots of cut-and-paste here. This needs some
41 ** functor voodoo to cut it all down a bit.
42 */
44 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
45 NR::Point const &p,
46 SPItem const *it) const
48 {
49 std::list<SPItem const *> lit;
50 lit.push_back(it);
51 return freeSnap(t, p, lit);
52 }
55 Inkscape::SnappedPoint SnapManager::freeSnap(Inkscape::Snapper::PointType t,
56 NR::Point const &p,
57 std::list<SPItem const *> const &it) const
58 {
59 Inkscape::SnappedPoint r(p, NR_HUGE);
61 SPNamedView::SnapperList snappers = namedview->getSnappers();
62 for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
63 Inkscape::SnappedPoint const s = (*i)->freeSnap(t, p, it);
64 if (s.getDistance() < r.getDistance()) {
65 r = s;
66 }
67 }
69 return r;
70 }
73 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
74 NR::Point const &p,
75 NR::Point const &c,
76 SPItem const *it) const
77 {
78 std::list<SPItem const *> lit;
79 lit.push_back(it);
80 return constrainedSnap(t, p, c, lit);
81 }
84 Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType t,
85 NR::Point const &p,
86 NR::Point const &c,
87 std::list<SPItem const *> const &it) const
88 {
89 Inkscape::SnappedPoint r(p, NR_HUGE);
91 SPNamedView::SnapperList snappers = namedview->getSnappers();
92 for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
93 Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, p, c, it);
94 if (s.getDistance() < r.getDistance()) {
95 r = s;
96 }
97 }
99 return r;
100 }
103 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
104 std::vector<NR::Point> const &p,
105 std::list<SPItem const *> const &it,
106 NR::Point const &tr) const
107 {
108 if (willSnapSomething() == false) {
109 return std::make_pair(tr, false);
110 }
112 NR::Point best_translation = tr;
113 NR::Coord best_distance = NR_HUGE;
115 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
116 /* Translated version of this point */
117 NR::Point const q = *i + tr;
118 /* Snap it */
119 Inkscape::SnappedPoint s = freeSnap(t, q, it);
120 if (s.getDistance() < NR_HUGE) {
121 /* Resulting translation */
122 NR::Point const r = s.getPoint() - *i;
123 NR::Coord const d = NR::L2(r);
124 if (d < best_distance) {
125 best_distance = d;
126 best_translation = r;
127 }
128 }
129 }
131 return std::make_pair(best_translation, best_distance < NR_HUGE);
132 }
136 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
137 std::vector<NR::Point> const &p,
138 NR::Point const &c,
139 std::list<SPItem const *> const &it,
140 NR::Point const &tr) const
141 {
142 if (willSnapSomething() == false) {
143 return std::make_pair(tr, false);
144 }
146 NR::Point best_translation = tr;
147 NR::Coord best_distance = NR_HUGE;
149 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
150 /* Translated version of this point */
151 NR::Point const q = *i + tr;
152 /* Snap it */
153 Inkscape::SnappedPoint s = constrainedSnap(t, q, c, it);
154 if (s.getDistance() < NR_HUGE) {
155 /* Resulting translation */
156 NR::Point const r = s.getPoint() - *i;
157 NR::Coord const d = NR::L2(r);
158 if (d < best_distance) {
159 best_distance = d;
160 best_translation = r;
161 }
162 }
163 }
165 return std::make_pair(best_translation, best_distance < NR_HUGE);
166 }
173 /// Minimal distance to norm before point is considered for snap.
174 static const double MIN_DIST_NORM = 1.0;
176 /**
177 * Try to snap \a req in one dimension.
178 *
179 * \param nv NamedView to use.
180 * \param req Point to snap; updated to the snapped point if a snap occurred.
181 * \param dim Dimension to snap in.
182 * \return Distance to the snap point along the \a dim axis, or \c NR_HUGE
183 * if no snap occurred.
184 */
185 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
186 NR::Dim2 const dim, SPItem const *it)
187 {
188 return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
189 }
191 NR::Coord namedview_dim_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t, NR::Point &req,
192 NR::Dim2 const dim, std::list<SPItem const *> const &it)
193 {
194 return namedview_vector_snap(nv, t, req, component_vectors[dim], it);
195 }
198 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
199 NR::Point &req, NR::Point const &d,
200 SPItem const *it)
201 {
202 std::list<SPItem const *> lit;
203 lit.push_back(it);
204 return namedview_vector_snap(nv, t, req, d, lit);
205 }
207 /**
208 * Look for snap point along the line described by the point \a req
209 * and the direction vector \a d.
210 * Modifies req to the snap point, if one is found.
211 * \return The distance from \a req to the snap point along the vector \a d,
212 * or \c NR_HUGE if no snap point was found.
213 *
215 */
216 NR::Coord namedview_vector_snap(SPNamedView const *nv, Inkscape::Snapper::PointType t,
217 NR::Point &req, NR::Point const &d,
218 std::list<SPItem const *> const &it)
219 {
220 g_assert(nv != NULL);
221 g_assert(SP_IS_NAMEDVIEW(nv));
223 SPNamedView::SnapperList snappers = nv->getSnappers();
225 NR::Coord best = NR_HUGE;
226 for (SPNamedView::SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
227 Inkscape::SnappedPoint const s = (*i)->constrainedSnap(t, req, d, it);
228 if (s.getDistance() < best) {
229 req = s.getPoint();
230 best = s.getDistance();
231 }
232 }
234 return best;
235 }
238 /*
239 * functions for lists of points
240 *
241 * All functions take a list of NR::Point and parameter indicating the proposed transformation.
242 * They return the updated transformation parameter.
243 */
245 /**
246 * Snap list of points in one dimension.
247 * \return Coordinate difference.
248 */
249 std::pair<NR::Coord, bool> namedview_dim_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
250 const std::vector<NR::Point> &p,
251 NR::Coord const dx, NR::Dim2 const dim,
252 std::list<SPItem const *> const &it
253 )
254 {
255 NR::Coord dist = NR_HUGE;
256 NR::Coord xdist = dx;
258 SnapManager const m(nv);
260 if (m.willSnapSomething()) {
261 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
262 NR::Point q = *i;
263 NR::Coord const pre = q[dim];
264 q[dim] += dx;
265 NR::Coord const d = namedview_dim_snap(nv, t, q, dim, it);
266 if (d < dist) {
267 xdist = q[dim] - pre;
268 dist = d;
269 }
270 }
271 }
273 return std::make_pair(xdist, dist < NR_HUGE);
274 }
276 /**
277 * Snap list of points in two dimensions.
278 */
279 std::pair<double, bool> namedview_vector_snap_list(SPNamedView const *nv, Inkscape::Snapper::PointType t,
280 const std::vector<NR::Point> &p, NR::Point const &norm,
281 NR::scale const &s, std::list<SPItem const *> const &it)
282 {
283 using NR::X;
284 using NR::Y;
286 SnapManager const m(nv);
288 if (m.willSnapSomething() == false) {
289 return std::make_pair(s[X], false);
290 }
292 NR::Coord dist = NR_HUGE;
293 double ratio = fabs(s[X]);
294 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
295 NR::Point const &q = *i;
296 NR::Point check = ( q - norm ) * s + norm;
297 if (NR::LInfty( q - norm ) > MIN_DIST_NORM) {
298 NR::Coord d = namedview_vector_snap(nv, t, check, check - norm, it);
299 if (d < dist) {
300 dist = d;
301 NR::Dim2 const dominant = ( ( fabs( q[X] - norm[X] ) >
302 fabs( q[Y] - norm[Y] ) )
303 ? X
304 : Y );
305 ratio = ( ( check[dominant] - norm[dominant] )
306 / ( q[dominant] - norm[dominant] ) );
307 }
308 }
309 }
311 return std::make_pair(ratio, dist < NR_HUGE);
312 }
315 /**
316 * Try to snap points in \a p after they have been scaled by \a sx with respect to
317 * the origin \a norm. The best snap is the one that changes the scale least.
318 *
319 * \return Pair containing snapped scale and a flag which is true if a snap was made.
320 */
321 std::pair<double, bool> namedview_dim_snap_list_scale(SPNamedView const *nv, Inkscape::Snapper::PointType t,
322 const std::vector<NR::Point> &p, NR::Point const &norm,
323 double const sx, NR::Dim2 dim,
324 std::list<const SPItem *> const &it)
325 {
326 SnapManager const m(nv);
327 if (m.willSnapSomething() == false) {
328 return std::make_pair(sx, false);
329 }
331 g_assert(dim < 2);
333 NR::Coord dist = NR_HUGE;
334 double scale = sx;
336 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
337 NR::Point q = *i;
338 NR::Point check = q;
340 /* Scaled version of the point we are looking at */
341 check[dim] = (sx * (q - norm) + norm)[dim];
343 if (fabs (q[dim] - norm[dim]) > MIN_DIST_NORM) {
344 /* Snap this point */
345 const NR::Coord d = namedview_dim_snap (nv, t, check, dim, it);
346 /* Work out the resulting scale factor */
347 double snapped_scale = (check[dim] - norm[dim]) / (q[dim] - norm[dim]);
349 if (dist == NR_HUGE || fabs(snapped_scale - sx) < fabs(scale - sx)) {
350 /* This is either the first point, or the snapped scale
351 ** is the closest yet to the original.
352 */
353 scale = snapped_scale;
354 dist = d;
355 }
356 }
357 }
359 return std::make_pair(scale, dist < NR_HUGE);
360 }
362 /**
363 * Try to snap points after they have been skewed.
364 */
365 double namedview_dim_snap_list_skew(SPNamedView const *nv, Inkscape::Snapper::PointType t,
366 const std::vector<NR::Point> &p, NR::Point const &norm,
367 double const sx, NR::Dim2 const dim)
368 {
369 SnapManager const m(nv);
371 if (m.willSnapSomething() == false) {
372 return sx;
373 }
375 g_assert(dim < 2);
377 gdouble dist = NR_HUGE;
378 gdouble skew = sx;
380 for (std::vector<NR::Point>::const_iterator i = p.begin(); i != p.end(); i++) {
381 NR::Point q = *i;
382 NR::Point check = q;
383 // apply shear
384 check[dim] += sx * (q[!dim] - norm[!dim]);
385 if (fabs (q[!dim] - norm[!dim]) > MIN_DIST_NORM) {
386 const gdouble d = namedview_dim_snap (nv, t, check, dim, NULL);
387 if (d < fabs (dist)) {
388 dist = d;
389 skew = (check[dim] - q[dim]) / (q[!dim] - norm[!dim]);
390 }
391 }
392 }
394 return skew;
395 }
397 /*
398 Local Variables:
399 mode:c++
400 c-file-style:"stroustrup"
401 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
402 indent-tabs-mode:nil
403 fill-column:99
404 End:
405 */
406 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :