Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
distribution-snapper.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
14
15#include <memory>
16
17#include "desktop.h"
18#include "document.h"
19#include "preferences.h"
20#include "selection.h"
21#include "snap.h"
22
23#include "object/sp-item.h"
24#include "object/sp-root.h"
25#include "object/sp-use.h"
26
27#define DISTRIBUTION_SNAPPING_EPSILON 0.5e-4f
28
29static bool compare_double(double x, double y, double epsilon = DISTRIBUTION_SNAPPING_EPSILON)
30{
31 if (abs(x - y) < epsilon)
32 return true;
33 return false;
34}
35
36static int sortBoxesRight(Geom::Rect const &a, Geom::Rect const &b)
37{
38 if (a.midpoint().x() < b.midpoint().x())
39 return 1;
40 return 0;
41}
42
43static int sortBoxesLeft(Geom::Rect const &a, Geom::Rect const &b)
44{
45 if (a.midpoint().x() > b.midpoint().x())
46 return 1;
47 return 0;
48}
49
50static int sortBoxesUp(Geom::Rect const &a, Geom::Rect const &b)
51{
52 if (a.midpoint().y() > b.midpoint().y())
53 return 1;
54 return 0;
55}
56
57static int sortBoxesDown(Geom::Rect const &a, Geom::Rect const &b)
58{
59 if (a.midpoint().y() < b.midpoint().y())
60 return 1;
61 return 0;
62}
63
65 : Snapper(sm, d)
66{
67 _bboxes_right = std::make_unique<std::vector<Geom::Rect>>();
68 _bboxes_left = std::make_unique<std::vector<Geom::Rect>>();
69 _bboxes_up = std::make_unique<std::vector<Geom::Rect>>();
70 _bboxes_down = std::make_unique<std::vector<Geom::Rect>>();
71}
72
74{
75 _bboxes_right->clear();
76 _bboxes_left->clear();
77 _bboxes_up->clear();
78 _bboxes_down->clear();
79}
80
82{
83 return -a.max().x() + b.min().x();
84}
85
87{
88 return a.min().x() - b.max().x();
89}
90
92{
93 return a.min().y() - b.max().y();
94}
95
97{
98 return -a.max().y() + b.min().y();
99}
100
102 Geom::Rect const &source_bbox,
103 std::vector<Geom::Rect>::iterator it,
104 std::vector<Geom::Rect>::iterator end,
105 std::vector<Geom::Rect> &vec,
106 Geom::Coord &dist,
107 Geom::Coord tol,
108 std::function<Geom::Coord(Geom::Rect const &, Geom::Rect const &)> const &distance_func,
109 int level) const
110{
111 std::vector<Geom::Rect>::iterator next_bbox = it;
112 std::vector<Geom::Rect>::iterator _next_bbox = it;
113
114 if (level == 0) {
115 int max_length = 0;
116
117 // check each consecutive box for a snap
118 Geom::Rect optimum_start;
119 while (std::next(next_bbox) != end) {
120 auto first_dist = distance_func(source_bbox, *next_bbox);
121 level = 0;
122
123 // temporary result for this particular item
124 std::vector<Geom::Rect> result;
125 if (_findSidewaysSnaps(*next_bbox, ++it, end, result, first_dist, tol, distance_func, ++level)) {
126 if (result.size() > max_length) {
127 // if this item has the most number of items equidistant form each other
128 // then make this the final result
129 optimum_start = *next_bbox;
130 max_length = result.size();
131 vec = result;
132 dist = first_dist;
133 }
134 }
135
136 ++next_bbox;
137 }
138
139 // if there is no snap, just add the first item and return false
140 // this is useful to find in-between snaps (see _snapEquidistantPoints())
141 if (max_length == 0) {
142 vec.push_back(*_next_bbox);
143 return false;
144 } else {
145 // insert the first item to the list, this does not happen automatically if level==1 (see below)
146 vec.insert(vec.begin(), optimum_start);
147 return true;
148 }
149 }
150
151 // if not the zeroth level
152 if (level != 1)
153 vec.push_back(source_bbox);
154
155 if (it == end || level > 10)
156 return true;
157
158 int og_level = level;
159 std::vector<Geom::Rect> best_result;
160
161 while (next_bbox != end) {
162 level = og_level;
163 Geom::Coord this_dist;
164 Geom::Coord next_dist = distance_func(source_bbox, *next_bbox);
165
166 std::vector<Geom::Rect> temp_result;
167
168 if (level == 1 && compare_double(dist, next_dist, tol)){
169 // if this is the first level, check if the snap is within tolerance
170 // we cancel here if the possible snap in not whithing tolerance, saves us some time!
171 this_dist = next_dist;
172 if (_findSidewaysSnaps(*next_bbox, ++it, end, temp_result, this_dist, tol, distance_func, ++level)) {
173 if (temp_result.size() > 0) {
174 dist = this_dist;
175 best_result = temp_result;
176 break;
177 }
178 }
179
180 } else if (compare_double(dist, next_dist, level * DISTRIBUTION_SNAPPING_EPSILON)) {
181
182 if (_findSidewaysSnaps(*next_bbox, ++it, end, temp_result, dist, tol, distance_func, ++level)) {
183 if (temp_result.size() > 0) {
184 best_result = temp_result;
185 break;
186 }
187 }
188 }
189
190 if (best_result.size() > 10)
191 break;
192
193 ++next_bbox;
194 }
195
196 vec.insert(vec.end(), best_result.begin(), best_result.end());
197 return true;
198}
199
200void Inkscape::DistributionSnapper::_collectBBoxes(Geom::OptRect const &bbox_to_snap, bool const &first_point) const
201{
202 if (!first_point)
203 return;
204
205 _bboxes_right->clear();
206 _bboxes_left->clear();
207 _bboxes_down->clear();
208 _bboxes_up->clear();
209
211
212 Preferences *prefs = Preferences::get();
213 bool prefs_bbox = prefs->getBool("/tools/bounding_box");
214 bbox_type = !prefs_bbox ? SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX;
215
216 // collect bounding boxes of other objects
217 for (const auto &candidate : *(_snapmanager->_align_snapper_candidates)) {
218 SPItem *root_item = candidate.item;
219
220 // get the root item in case we have a duplicate at hand
221 auto use = cast<SPUse>(candidate.item);
222 if (use) {
223 root_item = use->root();
224 }
225 g_return_if_fail(root_item);
226
227 // if candidate is not a clip or a mask object then extract its BBox points
228 if (!candidate.clip_or_mask) {
229 Geom::OptRect b = root_item->desktopBounds(bbox_type);
230 if (!b.intersects(bbox_to_snap)) {
231 auto diff_vec = b->midpoint() - bbox_to_snap->midpoint();
232
233 Geom::Rect Xbounds = *bbox_to_snap;
234 Xbounds.expandBy(_snapmanager->_desktop->get_display_area().maxExtent(), 0);
235
236 Geom::Rect Ybounds = *bbox_to_snap;
237 Ybounds.expandBy(0, _snapmanager->_desktop->get_display_area().maxExtent());
238
239 if (Xbounds.intersects(b)) {
240 if (diff_vec.x() > 0) {
241 _bboxes_right->push_back(*b);
242 } else {
243 _bboxes_left->push_back(*b);
244 }
245 } else if (Ybounds.intersects(b)) {
246 if (diff_vec.y() < 0) {
247 _bboxes_up->push_back(*b);
248 } else {
249 _bboxes_down->push_back(*b);
250 }
251 }
252 }
253 }
254 }
255
256 std::stable_sort(_bboxes_right->begin(), _bboxes_right->end(), sortBoxesRight);
257 std::stable_sort(_bboxes_left->begin(), _bboxes_left->end(), sortBoxesLeft);
258 std::stable_sort(_bboxes_up->begin(), _bboxes_up->end(), sortBoxesUp);
259 std::stable_sort(_bboxes_down->begin(), _bboxes_down->end(), sortBoxesDown);
260
261 _addBBoxForIntersectingBoxes(_bboxes_right.get(), Direction::RIGHT);
262 _addBBoxForIntersectingBoxes(_bboxes_left.get(), Direction::LEFT);
263 _addBBoxForIntersectingBoxes(_bboxes_up.get(), Direction::UP);
264 _addBBoxForIntersectingBoxes(_bboxes_down.get(), Direction::DOWN);
265}
266
267void Inkscape::DistributionSnapper::_addBBoxForIntersectingBoxes(std::vector<Geom::Rect> *vec, Direction dir) const {
268 if (vec->size() < 1) {
269 return;
270 }
271
272 int count = 0;
273 std::vector<std::pair<int, Geom::Rect>> insertPositions;
274
275 for (auto it = vec->begin(); it != vec->end(); it++, count++) {
276 Geom::Rect comb(*it);
277 int num = 0;
278 int insertPos = count;
279
280 while (std::next(it) != vec->end() && it->intersects(*std::next(it))) {
281 comb.unionWith(*std::next(it));
282 ++it;
283 ++num;
284 ++count;
285 }
286
287 if (num > 0) {
288 insertPositions.emplace_back(insertPos, comb);
289 }
290 }
291
292 if (insertPositions.size() != 0) {
293 // TODO: Does this improve performance?
294 vec->reserve(vec->size() + insertPositions.size());
295
296 count = 0;
297 for (auto pair : insertPositions) {
298 vec->insert(vec->begin() + pair.first + count, pair.second);
299 ++count;
300 }
301 }
302}
303
305 SnapCandidatePoint const &p,
306 Geom::OptRect const &bbox_to_snap,
307 std::vector<SnapCandidatePoint> *unselected_nodes,
308 SnapConstraint const &c,
309 Geom::Point const &p_proj_on_constraint) const
310{
311 bool consider_x = true;
312 bool consider_y = true;
313 if (!c.isUndefined() && c.isLinear()) {
314 if (c.getDirection().x() == 0)
315 consider_x = false; // consider horizontal snapping if moving vertically
316 else
317 consider_y = false; // consider vertical snapping if moving horizontally
318 }
319
320 _collectBBoxes(bbox_to_snap, p.getSourceNum() <= 0);
321
323
325 return;
326
327 bool always = getSnapperAlwaysSnap(p.getSourceType());
328 Geom::Coord equal_dist;
329
330 SnappedPoint sr, sl, sx, su, sd, sy;
331 Geom::Coord dist_x, dist_y;
332 bool snap_x = false, snap_y = false;
333
334 // 1. look right
335 // if there is a snap then add right bboxes and look left, if there is a snap to the left then
336 // add those bboxes too
337 std::vector<Geom::Rect> vecRight;
338 std::vector<Geom::Rect> vecLeft;
339 if (consider_x && _bboxes_right->size() > 0) {
340 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_right->begin(), _bboxes_right->end(), vecRight, equal_dist, getSnapperTolerance(), &DistributionSnapper::distRight)) {
341 auto first_dist = distRight(*bbox_to_snap, vecRight.front());
342 Geom::Coord offset = first_dist - equal_dist;
343 Geom::Point target = bbox_to_snap->midpoint() + Geom::Point(offset, 0);
344
345 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
346 Geom::Rect bbox = *bbox_to_snap * translation;
347 vecRight.insert(vecRight.begin(), bbox);
348
349 _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
350
351 if (_bboxes_left->size() > 0) {
352 first_dist = distLeft(bbox, _bboxes_left->front());
353 Geom::Coord left_dist;
354 vecLeft.clear();
355 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_left->begin(), _bboxes_left->end(), vecLeft, left_dist, getSnapperTolerance(), &DistributionSnapper::distLeft)) {
356 if (compare_double(left_dist, equal_dist)) {
357 std::reverse(vecLeft.begin(), vecLeft.end());
358 vecRight.insert(vecRight.begin(), vecLeft.begin(), vecLeft.end());
359 }
360
361 } else if (compare_double(first_dist, equal_dist)) {
362 vecRight.insert(vecRight.begin(), vecLeft.front());
363 }
364 }
365
366 dist_x = abs(offset);
367 sx = SnappedPoint(target, vecRight, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_RIGHT, dist_x, getSnapperTolerance(), always, false, true);
368 snap_x = true;
369 }
370 }
371
372 // 2. if no snap to right, look left
373 // if there is a snap then add left bboxes and right left, if there is a snap to the right then
374 // add those bboxes too
375 if (consider_x && !snap_x && _bboxes_left->size() > 0) {
376 vecLeft.clear();
377 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_left->begin(), _bboxes_left->end(), vecLeft, equal_dist, getSnapperTolerance(), &DistributionSnapper::distLeft)) {
378 auto first_dist = distLeft(*bbox_to_snap, vecLeft.front());
379 Geom::Coord offset = first_dist - equal_dist;
380 Geom::Point target = bbox_to_snap->midpoint() - Geom::Point(offset, 0);
381
382 // translate the source bbox to the snap position
383 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
384 Geom::Rect bbox = *bbox_to_snap * translation;
385 std::reverse(vecLeft.begin(), vecLeft.end());
386 vecLeft.push_back(bbox);
387
388 _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
389
390 if (_bboxes_right->size() > 0) {
391 first_dist = distRight(bbox, _bboxes_right->front());
392 Geom::Coord right_dist;
393 vecRight.clear();
394 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_right->begin(), _bboxes_right->end(), vecRight, right_dist, getSnapperTolerance(), &DistributionSnapper::distRight)) {
395 if (compare_double(right_dist, equal_dist)) {
396 vecLeft.insert(vecLeft.end(), vecRight.begin(), vecRight.end());
397 }
398
399 } else if (compare_double(first_dist, equal_dist)) {
400 vecLeft.push_back(vecRight.front());
401 }
402 }
403
404 dist_x = abs(offset);
405 sx = SnappedPoint(target, vecLeft, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_LEFT, dist_x, getSnapperTolerance(), always, false, true);
406 snap_x = true;
407 }
408 }
409
410 // 3. if no snap to right or left just add the center snap
411 if (consider_x && !snap_x && vecRight.size() > 0 && vecLeft.size() > 0) {
412 auto x = Geom::Point((vecRight.front().min() + vecLeft.front().max()) / 2).x();
413 offset = abs(x - bbox_to_snap->midpoint().x());
414 if (offset < getSnapperTolerance()) {
415 Geom::Point target = Geom::Point(x, bbox_to_snap->midpoint().y());
416 // translate the source bbox to the snap position
417 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
418 Geom::Rect bbox = *bbox_to_snap * translation;
419 std::vector<Geom::Rect> bboxes = {vecLeft.front(), bbox, vecRight.front()};
420
421 _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
422
423 equal_dist = bbox.min().x() - vecLeft.front().max().x();
424 sx = SnappedPoint(target, bboxes, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_X, offset, getSnapperTolerance(), always, false, true);
425 snap_x = true;
426 }
427 }
428
429 // 1. look Up
430 // if there is a snap then add top bboxes and look down, if there is a snap at the bottom then
431 // add those bboxes too
432 std::vector<Geom::Rect> vecUp;
433 std::vector<Geom::Rect> vecDown;
434 if (consider_y && _bboxes_up->size() > 0) {
435 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_up->begin(), _bboxes_up->end(), vecUp, equal_dist, getSnapperTolerance(), &DistributionSnapper::distUp)) {
436 auto first_dist = distUp(*bbox_to_snap, vecUp.front());
437 Geom::Coord offset = first_dist - equal_dist;
438 Geom::Point target = bbox_to_snap->midpoint() - Geom::Point(0, offset);
439
440 // translate the source bbox to the snap position
441 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
442 Geom::Rect bbox = *bbox_to_snap * translation;
443 std::reverse(vecUp.begin(), vecUp.end());
444 vecUp.push_back(bbox);
445
446 _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
447
448 if (_bboxes_down->size() > 0) {
449 first_dist = distDown(bbox, _bboxes_down->front());
450 Geom::Coord down_dist;
451 vecDown.clear();
452 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_down->begin(), _bboxes_down->end(), vecDown, down_dist,
453 getSnapperTolerance(), &DistributionSnapper::distDown)) {
454 if (abs(down_dist - equal_dist) < 1e-4) {
455 vecUp.insert(vecUp.end(), vecDown.begin(), vecDown.end());
456 }
457
458 } else if (abs(first_dist - equal_dist) < 1e-4) {
459 vecUp.insert(vecUp.end(), vecDown.front());
460 }
461 }
462
463 dist_y = abs(offset);
464 sy = SnappedPoint(target, vecUp, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_UP, dist_y, getSnapperTolerance(), always, false, true);
465 snap_y = true;
466 }
467 }
468
469 // 2. if no snaps on top, look Down
470 // if there is a snap then add bottom bboxes and look Up, if there is a snap above then
471 // add those bboxes too
472 if (consider_y && !snap_y && _bboxes_down->size() > 0) {
473 vecDown.clear();
474 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_down->begin(), _bboxes_down->end(), vecDown, equal_dist, getSnapperTolerance(), &DistributionSnapper::distDown)) {
475 auto first_dist = distDown(*bbox_to_snap, vecDown.front());
476 Geom::Coord offset = first_dist - equal_dist;
477 Geom::Point target = bbox_to_snap->midpoint() + Geom::Point(0, offset);
478
479 // translate the source bbox to the snap position
480 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
481 Geom::Rect bbox = *bbox_to_snap * translation;
482 vecDown.insert(vecDown.begin(), bbox);
483
484 _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
485
486 if (_bboxes_up->size() > 0) {
487 first_dist = distUp(bbox, _bboxes_up->front());
488 Geom::Coord up_dist;
489 vecUp.clear();
490
491 if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_up->begin(), _bboxes_up->end(), vecUp, up_dist, getSnapperTolerance(), &DistributionSnapper::distUp)) {
492 if (compare_double(up_dist, equal_dist)) {
493 std::reverse(vecUp.begin(), vecUp.end());
494 vecDown.insert(vecDown.begin(), vecUp.begin(), vecUp.end());
495 }
496 } else if (compare_double(first_dist, equal_dist)) {
497 vecDown.insert(vecDown.begin(), vecUp.front());
498 }
499 }
500
501 dist_y = abs(offset);
502 sy = SnappedPoint(target, vecDown, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_DOWN, dist_y, getSnapperTolerance(), always, false, true);
503 snap_y = true;
504 }
505 }
506
507 // 3. if no snap to right or left just add the center snap
508 if (consider_y && !snap_y && vecUp.size() > 0 && vecDown.size() > 0) {
509 auto y = Geom::Point((vecUp.front().max() + vecDown.front().min()) / 2).y();
510 offset = abs(y - bbox_to_snap->midpoint().y());
511 if (consider_y && offset < getSnapperTolerance()) {
512 Geom::Point target = Geom::Point(bbox_to_snap->midpoint().x(), y);
513 // translate the source bbox to the snap position
514 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
515 Geom::Rect bbox = *bbox_to_snap * translation;
516 std::vector<Geom::Rect> bboxes = {vecUp.front(), bbox, vecDown.front()};
517
518 _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
519
520 equal_dist = bbox.min().y() - vecUp.front().max().y();
521 sy = SnappedPoint(target, bboxes, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_Y, offset, getSnapperTolerance(), always, false, true);
522 snap_y = true;
523 }
524 }
525
526 if (snap_x && snap_y) {
527 Geom::Point target = Geom::Point(sx.getPoint().x(), sy.getPoint().y());
528 Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
529 Geom::Rect bbox = *bbox_to_snap * translation;
530 std::vector<Geom::Rect> bboxes_x = sx.getBBoxes();
531 std::vector<Geom::Rect> bboxes_y = sy.getBBoxes();
532
533 // Do not need to correct here, already did that earlier for each direction separately
534 //_correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
535 auto si = SnappedPoint(target, bboxes_x, bboxes_y, bbox, sx.getDistributionDistance(), sy.getDistributionDistance(), p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_XY, offset, getSnapperTolerance(), always, false, true);
536 isr.points.push_back(si);
537 return;
538 }
539
540 if (snap_x) {
541 isr.points.push_back(sx);
542 }
543
544 if (snap_y) {
545 isr.points.push_back(sy);
546 }
547}
548
550 Geom::Point const &p,
551 Geom::Rect const &bbox_to_snap) const
552{
553 if (_snapmanager->_desktop->getSelection()->size() > 1) {
554 auto correction = bbox_to_snap.midpoint() - p;
555 target -= correction;
556 }
557}
558
561 Geom::OptRect const &bbox_to_snap,
562 std::vector<SPObject const *> const *it,
563 std::vector<SnapCandidatePoint> *unselected_nodes) const
564{
565 if (bbox_to_snap.empty())
566 return;
567
569 return;
570 }
571
572 // toggle checks
573 if (!_snap_enabled || !_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_DISTRIBUTION_CATEGORY) ||
574 !_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_ALIGNMENT_CATEGORY))
575 return;
576
577 if (p.getSourceNum() <= 0) {
578 Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint());
579 _snapmanager->_findCandidates(_snapmanager->getDocument()->getRoot(), it, local_bbox_to_snap, false, Geom::identity());
580 }
581
582 _snapEquidistantPoints(isr, p, bbox_to_snap, unselected_nodes);
583}
584
587 Geom::OptRect const &bbox_to_snap,
588 SnapConstraint const &c,
589 std::vector<SPObject const *> const *it,
590 std::vector<SnapCandidatePoint> *unselected_nodes) const
591{
592 if (bbox_to_snap.empty())
593 return;
594
595 // toggle checks
596 if (!_snap_enabled || !_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_DISTRIBUTION_CATEGORY) ||
597 !_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_ALIGNMENT_CATEGORY))
598 return;
599
600 // project the mouse pointer onto the constraint. Only the projected point will be considered for snapping
601 Geom::Point pp = c.projection(p.getPoint());
602
603 if (p.getSourceNum() <= 0) {
604 Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint());
605 _snapmanager->_findCandidates(_snapmanager->getDocument()->getRoot(), it, local_bbox_to_snap, false, Geom::identity());
606 }
607
608 _snapEquidistantPoints(isr, p, bbox_to_snap, unselected_nodes, c, pp);
609}
610
612{
613 return true;
614}
615
617{
618 return Preferences::get()->getBool("/options/snap/distribution/always", false);
619}
620
622{
623 SPDesktop const *dt = _snapmanager->getDesktop();
624 double const zoom = dt ? dt->current_zoom() : 1;
625 return _snapmanager->snapprefs.getDistributionTolerance() / zoom;
626}
627
628/*
629 Local Variables:
630 mode:c++
631 c-file-style:"stroustrup"
632 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
633 indent-tabs-mode:nil
634 fill-column:99
635 End:
636*/
637// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
3x3 matrix representing an affine transformation.
Definition affine.h:70
bool empty() const
Check for emptiness.
bool intersects(CRect const &r) const
Check whether the rectangles have any common points.
bool intersects(GenericRect< C > const &r) const
Check whether the rectangles have any common points.
CPoint midpoint() const
Get the point in the geometric center of the rectangle.
void expandBy(C amount)
Expand the rectangle in both directions by the specified amount.
void unionWith(CRect const &b)
Enlarge the rectangle to contain the argument.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint max() const
Get the corner of the rectangle with largest coordinate values.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
Translation by a vector.
Definition transforms.h:115
static Geom::Coord distUp(Geom::Rect const &a, Geom::Rect const &b)
std::unique_ptr< std::vector< Geom::Rect > > _bboxes_left
void _snapEquidistantPoints(IntermSnapResults &isr, SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, std::vector< SnapCandidatePoint > *unselected_nodes, SnapConstraint const &c=SnapConstraint(), Geom::Point const &p_proj_on_constraint=Geom::Point()) const
Finds and snaps to points that is equidistant from surrounding bboxes.
static Geom::Coord distLeft(Geom::Rect const &a, Geom::Rect const &b)
bool ThisSnapperMightSnap() const override
void _addBBoxForIntersectingBoxes(std::vector< Geom::Rect > *vec, Direction dir) const
This functions adds overlapping bounding boxes to the list of bounding boxes.
DistributionSnapper(SnapManager *sm, Geom::Coord const d)
static Geom::Coord distDown(Geom::Rect const &a, Geom::Rect const &b)
bool _findSidewaysSnaps(Geom::Rect const &source_bbox, std::vector< Geom::Rect >::iterator it, std::vector< Geom::Rect >::iterator end, std::vector< Geom::Rect > &vec, Geom::Coord &dist, Geom::Coord tol, std::function< Geom::Coord(Geom::Rect const &, Geom::Rect const &)> const &distance_func, int level=0) const
Finds and stores the bounding boxes that are at equal distance from each other.
void _collectBBoxes(Geom::OptRect const &bbox_to_snap, bool const &first_point) const
Collects and caches bounding boxes to the left, right, up, and down of the selected object.
Geom::Coord getSnapperTolerance() const override
static Geom::Coord distRight(Geom::Rect const &a, Geom::Rect const &b)
bool getSnapperAlwaysSnap(SnapSourceType const &source) const override
std::unique_ptr< std::vector< Geom::Rect > > _bboxes_down
void freeSnap(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, std::vector< SPObject const * > const *it, std::vector< SnapCandidatePoint > *unselected_nodes) const override
std::unique_ptr< std::vector< Geom::Rect > > _bboxes_up
std::unique_ptr< std::vector< Geom::Rect > > _bboxes_right
void constrainedSnap(IntermSnapResults &isr, Inkscape::SnapCandidatePoint const &p, Geom::OptRect const &bbox_to_snap, SnapConstraint const &c, std::vector< SPObject const * > const *it, std::vector< SnapCandidatePoint > *unselected_nodes) const override
void _correctSelectionBBox(Geom::Point &target, Geom::Point const &p, Geom::Rect const &bbox_to_snap) const
When the selection has more than one objects in it, the bounding box of the object that the selection...
Preference storage class.
Definition preferences.h:61
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
Class to store data for points which are snap candidates, either as a source or as a target.
Inkscape::SnapSourceType getSourceType() const
Geom::Point const & getPoint() const
Class describing the result of an attempt to snap.
Geom::Coord getDistributionDistance() const
Geom::Point getPoint() const
std::vector< Geom::Rect > const & getBBoxes() const
Parent for classes that can snap points to something.
Definition snapper.h:39
To do: update description of desktop.
Definition desktop.h:149
double current_zoom() const
Definition desktop.h:335
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::OptRect desktopBounds(BBoxType type) const
Definition sp-item.cpp:1075
@ VISUAL_BBOX
Definition sp-item.h:118
@ GEOMETRIC_BBOX
Definition sp-item.h:116
Class to coordinate snapping operations.
Definition snap.h:80
Css & result
double c[8][4]
Editable view implementation.
static int sortBoxesUp(Geom::Rect const &a, Geom::Rect const &b)
static int sortBoxesLeft(Geom::Rect const &a, Geom::Rect const &b)
static int sortBoxesDown(Geom::Rect const &a, Geom::Rect const &b)
static bool compare_double(double x, double y, double epsilon=DISTRIBUTION_SNAPPING_EPSILON)
static int sortBoxesRight(Geom::Rect const &a, Geom::Rect const &b)
Authors: Parth Pant parthpant4@gmail.com
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
double offset
Geom::Point end
Affine identity()
Create an identity matrix.
Definition affine.h:210
SnapSourceType
enumerations of snap source types and snap target types.
Definition snap-enums.h:18
@ SNAPSOURCE_BBOX_MIDPOINT
Definition snap-enums.h:26
@ SNAPSOURCE_BBOX_CATEGORY
Definition snap-enums.h:23
@ SNAPTARGET_DISTRIBUTION_XY
Definition snap-enums.h:146
@ SNAPTARGET_DISTRIBUTION_LEFT
Definition snap-enums.h:143
@ SNAPTARGET_DISTRIBUTION_DOWN
Definition snap-enums.h:145
@ SNAPTARGET_ALIGNMENT_CATEGORY
Definition snap-enums.h:125
@ SNAPTARGET_DISTRIBUTION_UP
Definition snap-enums.h:144
@ SNAPTARGET_DISTRIBUTION_CATEGORY
Definition snap-enums.h:139
@ SNAPTARGET_DISTRIBUTION_X
Definition snap-enums.h:140
@ SNAPTARGET_DISTRIBUTION_Y
Definition snap-enums.h:141
@ SNAPTARGET_DISTRIBUTION_RIGHT
Definition snap-enums.h:142
Singleton class to access the preferences file in a convenient way.
int num
Definition scribble.cpp:47
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
SPRoot: SVG <svg> implementation.
std::list< Inkscape::SnappedPoint > points
Definition snapper.h:26