3d4a48005d5deb0229326ca60d1d22515cff5692
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 const *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 }
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 Inkscape::Snapper::ConstraintLine 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 Inkscape::Snapper::ConstraintLine 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::_snapTransformed(
112 Inkscape::Snapper::PointType type,
113 std::vector<NR::Point> const &points,
114 std::list<SPItem const *> const &ignore,
115 bool constrained,
116 Inkscape::Snapper::ConstraintLine const &constraint,
117 Transformation transformation_type,
118 NR::Point const &transformation,
119 NR::Point const &origin,
120 NR::Dim2 dim,
121 bool uniform) const
122 {
123 /* We have a list of points, which we are proposing to transform in some way. We need to see
124 ** if any of these points, when transformed, snap to anything. If they do, we return the
125 ** appropriate transformation with `true'; otherwise we return the original scale with `false'.
126 */
128 /* Quick check to see if we have any snappers that are enabled */
129 if (willSnapSomething() == false) {
130 return std::make_pair(transformation, false);
131 }
133 /* The current best transformation */
134 NR::Point best_transformation = transformation;
136 /* The current best metric for the best transformation; lower is better, NR_HUGE
137 ** means that we haven't snapped anything.
138 */
139 double best_metric = NR_HUGE;
141 for (std::vector<NR::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
143 /* Work out the transformed version of this point */
144 NR::Point transformed;
145 switch (transformation_type) {
146 case TRANSLATION:
147 transformed = *i + transformation;
148 break;
149 case SCALE:
150 transformed = ((*i - origin) * NR::scale(transformation[NR::X], transformation[NR::Y])) + origin;
151 break;
152 case STRETCH:
153 {
154 NR::scale s(1, 1);
155 if (uniform)
156 s[NR::X] = s[NR::Y] = transformation[dim];
157 else {
158 s[dim] = transformation[dim];
159 s[1 - dim] = 1;
160 }
161 transformed = ((*i - origin) * s) + origin;
162 break;
163 }
164 case SKEW:
165 transformed = *i;
166 transformed[dim] += transformation[dim] * ((*i)[1 - dim] - origin[1 - dim]);
167 break;
168 default:
169 g_assert_not_reached();
170 }
172 /* Snap it */
173 Inkscape::SnappedPoint const snapped = constrained ?
174 constrainedSnap(type, transformed, constraint, ignore) : freeSnap(type, transformed, ignore);
176 if (snapped.getDistance() < NR_HUGE) {
177 /* We snapped. Find the transformation that describes where the snapped point has
178 ** ended up, and also the metric for this transformation.
179 */
180 NR::Point result;
181 NR::Coord metric;
182 switch (transformation_type) {
183 case TRANSLATION:
184 result = snapped.getPoint() - *i;
185 metric = NR::L2(result);
186 break;
187 case SCALE:
188 {
189 NR::Point const a = (snapped.getPoint() - origin);
190 NR::Point const b = (*i - origin);
191 result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
192 metric = std::abs(NR::L2(result) - NR::L2(transformation));
193 break;
194 }
195 case STRETCH:
196 {
197 for (int j = 0; j < 2; j++) {
198 if (uniform || j == dim) {
199 result[j] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
200 } else {
201 result[j] = 1;
202 }
203 }
204 metric = std::abs(result[dim] - transformation[dim]);
205 break;
206 }
207 case SKEW:
208 result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
209 metric = std::abs(result[dim] - transformation[dim]);
210 break;
211 default:
212 g_assert_not_reached();
213 }
215 /* Note it if it's the best so far */
216 if (metric < best_metric && metric != 0) {
217 best_transformation = result;
218 best_metric = metric;
219 }
220 }
221 }
223 return std::make_pair(best_transformation, best_metric < NR_HUGE);
224 }
227 std::pair<NR::Point, bool> SnapManager::freeSnapTranslation(Inkscape::Snapper::PointType t,
228 std::vector<NR::Point> const &p,
229 std::list<SPItem const *> const &it,
230 NR::Point const &tr) const
231 {
232 return _snapTransformed(
233 t, p, it, false, NR::Point(), TRANSLATION, tr, NR::Point(), NR::X, false
234 );
235 }
239 std::pair<NR::Point, bool> SnapManager::constrainedSnapTranslation(Inkscape::Snapper::PointType t,
240 std::vector<NR::Point> const &p,
241 std::list<SPItem const *> const &it,
242 Inkscape::Snapper::ConstraintLine const &c,
243 NR::Point const &tr) const
244 {
245 return _snapTransformed(
246 t, p, it, true, c, TRANSLATION, tr, NR::Point(), NR::X, false
247 );
248 }
250 std::pair<NR::scale, bool> SnapManager::freeSnapScale(Inkscape::Snapper::PointType t,
251 std::vector<NR::Point> const &p,
252 std::list<SPItem const *> const &it,
253 NR::scale const &s,
254 NR::Point const &o) const
255 {
256 return _snapTransformed(
257 t, p, it, false, NR::Point(), SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
258 );
259 }
262 std::pair<NR::scale, bool> SnapManager::constrainedSnapScale(Inkscape::Snapper::PointType t,
263 std::vector<NR::Point> const &p,
264 std::list<SPItem const *> const &it,
265 Inkscape::Snapper::ConstraintLine const &c,
266 NR::scale const &s,
267 NR::Point const &o) const
268 {
269 return _snapTransformed(
270 t, p, it, true, c, SCALE, NR::Point(s[NR::X], s[NR::Y]), o, NR::X, false
271 );
272 }
275 std::pair<NR::Coord, bool> SnapManager::freeSnapStretch(Inkscape::Snapper::PointType t,
276 std::vector<NR::Point> const &p,
277 std::list<SPItem const *> const &it,
278 NR::Coord const &s,
279 NR::Point const &o,
280 NR::Dim2 d,
281 bool u) const
282 {
283 std::pair<NR::Point, bool> const r = _snapTransformed(
284 t, p, it, false, NR::Point(), STRETCH, NR::Point(s, s), o, d, u
285 );
287 return std::make_pair(r.first[d], r.second);
288 }
291 std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointType t,
292 std::vector<NR::Point> const &p,
293 std::list<SPItem const *> const &it,
294 NR::Coord const &s,
295 NR::Point const &o,
296 NR::Dim2 d) const
297 {
298 std::pair<NR::Point, bool> const r = _snapTransformed(
299 t, p, it, false, NR::Point(), SKEW, NR::Point(s, s), o, d, false
300 );
302 return std::make_pair(r.first[d], r.second);
303 }
305 /*
306 Local Variables:
307 mode:c++
308 c-file-style:"stroustrup"
309 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
310 indent-tabs-mode:nil
311 fill-column:99
312 End:
313 */
314 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :