summaryrefslogtreecommitdiffstats
path: root/src/distribution-snapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/distribution-snapper.cpp')
-rw-r--r--src/distribution-snapper.cpp648
1 files changed, 648 insertions, 0 deletions
diff --git a/src/distribution-snapper.cpp b/src/distribution-snapper.cpp
new file mode 100644
index 0000000..b5ed582
--- /dev/null
+++ b/src/distribution-snapper.cpp
@@ -0,0 +1,648 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * Snapping equidistant objects
+ *
+ * Authors:
+ * Parth Pant <parthpant4@gmail.com>
+ *
+ * Copyright (C) 2021 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <2geom/circle.h>
+#include <2geom/line.h>
+#include <2geom/path-intersection.h>
+#include <2geom/path-sink.h>
+#include <memory>
+
+#include "desktop.h"
+#include "display/curve.h"
+#include "document.h"
+#include "inkscape.h"
+#include "live_effects/effect-enum.h"
+#include "object/sp-clippath.h"
+#include "object/sp-flowtext.h"
+#include "object/sp-image.h"
+#include "object/sp-item-group.h"
+#include "object/sp-mask.h"
+#include "object/sp-namedview.h"
+#include "object/sp-path.h"
+#include "object/sp-root.h"
+#include "object/sp-shape.h"
+#include "object/sp-text.h"
+#include "object/sp-use.h"
+#include "path/path-util.h" // curve_for_item
+#include "preferences.h"
+#include "style.h"
+#include "svg/svg.h"
+
+#define DISTRIBUTION_SNAPPING_EPSILON 0.5e-4f
+
+static bool compare_double(double x, double y, double epsilon = DISTRIBUTION_SNAPPING_EPSILON)
+{
+ if (abs(x - y) < epsilon)
+ return true;
+ return false;
+}
+
+static int sortBoxesRight(Geom::Rect const &a, Geom::Rect const &b)
+{
+ if (a.midpoint().x() < b.midpoint().x())
+ return 1;
+ return 0;
+}
+
+static int sortBoxesLeft(Geom::Rect const &a, Geom::Rect const &b)
+{
+ if (a.midpoint().x() > b.midpoint().x())
+ return 1;
+ return 0;
+}
+
+static int sortBoxesUp(Geom::Rect const &a, Geom::Rect const &b)
+{
+ if (a.midpoint().y() > b.midpoint().y())
+ return 1;
+ return 0;
+}
+
+static int sortBoxesDown(Geom::Rect const &a, Geom::Rect const &b)
+{
+ if (a.midpoint().y() < b.midpoint().y())
+ return 1;
+ return 0;
+}
+
+Inkscape::DistributionSnapper::DistributionSnapper(SnapManager *sm, Geom::Coord const d)
+ : Snapper(sm, d)
+{
+ _bboxes_right = std::make_unique<std::vector<Geom::Rect>>();
+ _bboxes_left = std::make_unique<std::vector<Geom::Rect>>();
+ _bboxes_up = std::make_unique<std::vector<Geom::Rect>>();
+ _bboxes_down = std::make_unique<std::vector<Geom::Rect>>();
+}
+
+Inkscape::DistributionSnapper::~DistributionSnapper()
+{
+ _bboxes_right->clear();
+ _bboxes_left->clear();
+ _bboxes_up->clear();
+ _bboxes_down->clear();
+}
+
+Geom::Coord Inkscape::DistributionSnapper::distRight(Geom::Rect const &a, Geom::Rect const &b)
+{
+ return -a.max().x() + b.min().x();
+}
+
+Geom::Coord Inkscape::DistributionSnapper::distLeft(Geom::Rect const &a, Geom::Rect const &b)
+{
+ return a.min().x() - b.max().x();
+}
+
+Geom::Coord Inkscape::DistributionSnapper::distUp(Geom::Rect const &a, Geom::Rect const &b)
+{
+ return a.min().y() - b.max().y();
+}
+
+Geom::Coord Inkscape::DistributionSnapper::distDown(Geom::Rect const &a, Geom::Rect const &b)
+{
+ return -a.max().y() + b.min().y();
+}
+
+bool Inkscape::DistributionSnapper::_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) const
+{
+ std::vector<Geom::Rect>::iterator next_bbox = it;
+ std::vector<Geom::Rect>::iterator _next_bbox = it;
+
+ if (level == 0) {
+ int max_length = 0;
+
+ // check each consecutive box for a snap
+ Geom::Rect optimum_start;
+ while (std::next(next_bbox) != end) {
+ auto first_dist = distance_func(source_bbox, *next_bbox);
+ level = 0;
+
+ // temporary result for this particular item
+ std::vector<Geom::Rect> result;
+ if (_findSidewaysSnaps(*next_bbox, ++it, end, result, first_dist, tol, distance_func, ++level)) {
+ if (result.size() > max_length) {
+ // if this item has the most number of items equidistant form each other
+ // then make this the final result
+ optimum_start = *next_bbox;
+ max_length = result.size();
+ vec = result;
+ dist = first_dist;
+ }
+ }
+
+ ++next_bbox;
+ }
+
+ // if there is no snap, just add the first item and return false
+ // this is useful to find in-between snaps (see _snapEquidistantPoints())
+ if (max_length == 0) {
+ vec.push_back(*_next_bbox);
+ return false;
+ } else {
+ // insert the first item to the list, this does not happen automatically if level==1 (see below)
+ vec.insert(vec.begin(), optimum_start);
+ return true;
+ }
+ }
+
+ // if not the zeroth level
+ if (level != 1)
+ vec.push_back(source_bbox);
+
+ if (it == end || level > 10)
+ return true;
+
+ int og_level = level;
+ std::vector<Geom::Rect> best_result;
+
+ while (next_bbox != end) {
+ level = og_level;
+ Geom::Coord this_dist;
+ Geom::Coord next_dist = distance_func(source_bbox, *next_bbox);
+
+ std::vector<Geom::Rect> temp_result;
+
+ if (level == 1 && compare_double(dist, next_dist, tol)){
+ // if this is the first level, check if the snap is within tolerance
+ // we cancel here if the possible snap in not whithing tolerance, saves us some time!
+ this_dist = next_dist;
+ if (_findSidewaysSnaps(*next_bbox, ++it, end, temp_result, this_dist, tol, distance_func, ++level)) {
+ if (temp_result.size() > 0) {
+ dist = this_dist;
+ best_result = temp_result;
+ break;
+ }
+ }
+
+ } else if (compare_double(dist, next_dist, level * DISTRIBUTION_SNAPPING_EPSILON)) {
+
+ if (_findSidewaysSnaps(*next_bbox, ++it, end, temp_result, dist, tol, distance_func, ++level)) {
+ if (temp_result.size() > 0) {
+ best_result = temp_result;
+ break;
+ }
+ }
+ }
+
+ if (best_result.size() > 10)
+ break;
+
+ ++next_bbox;
+ }
+
+ vec.insert(vec.end(), best_result.begin(), best_result.end());
+ return true;
+}
+
+void Inkscape::DistributionSnapper::_collectBBoxes(Geom::OptRect const &bbox_to_snap, bool const &first_point) const
+{
+ if (!first_point)
+ return;
+
+ _bboxes_right->clear();
+ _bboxes_left->clear();
+ _bboxes_down->clear();
+ _bboxes_up->clear();
+
+ SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX;
+
+ Preferences *prefs = Preferences::get();
+ bool prefs_bbox = prefs->getBool("/tools/bounding_box");
+ bbox_type = !prefs_bbox ? SPItem::VISUAL_BBOX : SPItem::GEOMETRIC_BBOX;
+
+ // collect bounding boxes of other objects
+ for (const auto &candidate : *(_snapmanager->_align_snapper_candidates)) {
+ SPItem *root_item = candidate.item;
+
+ // get the root item in case we have a duplicate at hand
+ SPUse *use = dynamic_cast<SPUse *>(candidate.item);
+ if (use) {
+ root_item = use->root();
+ }
+ g_return_if_fail(root_item);
+
+ // if candidate is not a clip or a mask object then extract its BBox points
+ if (!candidate.clip_or_mask) {
+ Geom::OptRect b = root_item->desktopBounds(bbox_type);
+ if (!b.intersects(bbox_to_snap)) {
+ auto diff_vec = b->midpoint() - bbox_to_snap->midpoint();
+
+ Geom::Rect Xbounds = *bbox_to_snap;
+ Xbounds.expandBy(_snapmanager->_desktop->get_display_area().maxExtent(), 0);
+
+ Geom::Rect Ybounds = *bbox_to_snap;
+ Ybounds.expandBy(0, _snapmanager->_desktop->get_display_area().maxExtent());
+
+ if (Xbounds.intersects(b)) {
+ if (diff_vec.x() > 0) {
+ _bboxes_right->push_back(*b);
+ } else {
+ _bboxes_left->push_back(*b);
+ }
+ } else if (Ybounds.intersects(b)) {
+ if (diff_vec.y() < 0) {
+ _bboxes_up->push_back(*b);
+ } else {
+ _bboxes_down->push_back(*b);
+ }
+ }
+ }
+ }
+ }
+
+ std::stable_sort(_bboxes_right->begin(), _bboxes_right->end(), sortBoxesRight);
+ std::stable_sort(_bboxes_left->begin(), _bboxes_left->end(), sortBoxesLeft);
+ std::stable_sort(_bboxes_up->begin(), _bboxes_up->end(), sortBoxesUp);
+ std::stable_sort(_bboxes_down->begin(), _bboxes_down->end(), sortBoxesDown);
+
+ _addBBoxForIntersectingBoxes(_bboxes_right.get(), Direction::RIGHT);
+ _addBBoxForIntersectingBoxes(_bboxes_left.get(), Direction::LEFT);
+ _addBBoxForIntersectingBoxes(_bboxes_up.get(), Direction::UP);
+ _addBBoxForIntersectingBoxes(_bboxes_down.get(), Direction::DOWN);
+}
+
+void Inkscape::DistributionSnapper::_addBBoxForIntersectingBoxes(std::vector<Geom::Rect> *vec, Direction dir) const {
+ if (vec->size() < 1) {
+ return;
+ }
+
+ int count = 0;
+ std::vector<std::pair<int, Geom::Rect>> insertPositions;
+
+ for (auto it = vec->begin(); it != vec->end(); it++, count++) {
+ Geom::Rect comb(*it);
+ int num = 0;
+ int insertPos = count;
+
+ while (std::next(it) != vec->end() && it->intersects(*std::next(it))) {
+ comb.unionWith(*std::next(it));
+ ++it;
+ ++num;
+ ++count;
+ }
+
+ if (num > 0) {
+ insertPositions.emplace_back(insertPos, comb);
+ }
+ }
+
+ if (insertPositions.size() != 0) {
+ // TODO: Does this improve performance?
+ vec->reserve(vec->size() + insertPositions.size());
+
+ count = 0;
+ for (auto pair : insertPositions) {
+ vec->insert(vec->begin() + pair.first + count, pair.second);
+ ++count;
+ }
+ }
+}
+
+void Inkscape::DistributionSnapper::_snapEquidistantPoints(IntermSnapResults &isr,
+ SnapCandidatePoint const &p,
+ Geom::OptRect const &bbox_to_snap,
+ std::vector<SnapCandidatePoint> *unselected_nodes,
+ SnapConstraint const &c,
+ Geom::Point const &p_proj_on_constraint) const
+{
+ bool consider_x = true;
+ bool consider_y = true;
+ if (!c.isUndefined() && c.isLinear()) {
+ if (c.getDirection().x() == 0)
+ consider_x = false; // consider horizontal snapping if moving vertically
+ else
+ consider_y = false; // consider vertical snapping if moving horizontally
+ }
+
+ _collectBBoxes(bbox_to_snap, p.getSourceNum() <= 0);
+
+ Geom::Coord offset;
+
+ if (p.getSourceType() != SNAPSOURCE_BBOX_MIDPOINT)
+ return;
+
+ Geom::Coord equal_dist;
+
+ SnappedPoint sr, sl, sx, su, sd, sy;
+ Geom::Coord dist_x, dist_y;
+ bool snap_x = false, snap_y = false;
+
+ // 1. look right
+ // if there is a snap then add right bboxes and look left, if there is a snap to the left then
+ // add those bboxes too
+ std::vector<Geom::Rect> vecRight;
+ std::vector<Geom::Rect> vecLeft;
+ if (consider_x && _bboxes_right->size() > 0) {
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_right->begin(), _bboxes_right->end(), vecRight, equal_dist, getSnapperTolerance(), &DistributionSnapper::distRight)) {
+ auto first_dist = distRight(*bbox_to_snap, vecRight.front());
+ Geom::Coord offset = first_dist - equal_dist;
+ Geom::Point target = bbox_to_snap->midpoint() + Geom::Point(offset, 0);
+
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ vecRight.insert(vecRight.begin(), bbox);
+
+ _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+
+ if (_bboxes_left->size() > 0) {
+ first_dist = distLeft(bbox, _bboxes_left->front());
+ Geom::Coord left_dist;
+ vecLeft.clear();
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_left->begin(), _bboxes_left->end(), vecLeft, left_dist, getSnapperTolerance(), &DistributionSnapper::distLeft)) {
+ if (compare_double(left_dist, equal_dist)) {
+ std::reverse(vecLeft.begin(), vecLeft.end());
+ vecRight.insert(vecRight.begin(), vecLeft.begin(), vecLeft.end());
+ }
+
+ } else if (compare_double(first_dist, equal_dist)) {
+ vecRight.insert(vecRight.begin(), vecLeft.front());
+ }
+ }
+
+ dist_x = abs(offset);
+ sx = SnappedPoint(target, vecRight, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_RIGHT, dist_x, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ snap_x = true;
+ }
+ }
+
+ // 2. if no snap to right, look left
+ // if there is a snap then add left bboxes and right left, if there is a snap to the right then
+ // add those bboxes too
+ if (consider_x && !snap_x && _bboxes_left->size() > 0) {
+ vecLeft.clear();
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_left->begin(), _bboxes_left->end(), vecLeft, equal_dist, getSnapperTolerance(), &DistributionSnapper::distLeft)) {
+ auto first_dist = distLeft(*bbox_to_snap, vecLeft.front());
+ Geom::Coord offset = first_dist - equal_dist;
+ Geom::Point target = bbox_to_snap->midpoint() - Geom::Point(offset, 0);
+
+ // translate the source bbox to the snap position
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ std::reverse(vecLeft.begin(), vecLeft.end());
+ vecLeft.push_back(bbox);
+
+ _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+
+ if (_bboxes_right->size() > 0) {
+ first_dist = distRight(bbox, _bboxes_right->front());
+ Geom::Coord right_dist;
+ vecRight.clear();
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_right->begin(), _bboxes_right->end(), vecRight, right_dist, getSnapperTolerance(), &DistributionSnapper::distRight)) {
+ if (compare_double(right_dist, equal_dist)) {
+ vecLeft.insert(vecLeft.end(), vecRight.begin(), vecRight.end());
+ }
+
+ } else if (compare_double(first_dist, equal_dist)) {
+ vecLeft.push_back(vecRight.front());
+ }
+ }
+
+ dist_x = abs(offset);
+ sx = SnappedPoint(target, vecLeft, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_LEFT, dist_x, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ snap_x = true;
+ }
+ }
+
+ // 3. if no snap to right or left just add the center snap
+ if (consider_x && !snap_x && vecRight.size() > 0 && vecLeft.size() > 0) {
+ auto x = Geom::Point((vecRight.front().min() + vecLeft.front().max()) / 2).x();
+ offset = abs(x - bbox_to_snap->midpoint().x());
+ if (offset < getSnapperTolerance()) {
+ Geom::Point target = Geom::Point(x, bbox_to_snap->midpoint().y());
+ // translate the source bbox to the snap position
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ std::vector<Geom::Rect> bboxes = {vecLeft.front(), bbox, vecRight.front()};
+
+ _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+
+ equal_dist = bbox.min().x() - vecLeft.front().max().x();
+ sx = SnappedPoint(target, bboxes, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_X, offset, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ snap_x = true;
+ }
+ }
+
+ // 1. look Up
+ // if there is a snap then add top bboxes and look down, if there is a snap at the bottom then
+ // add those bboxes too
+ std::vector<Geom::Rect> vecUp;
+ std::vector<Geom::Rect> vecDown;
+ if (consider_y && _bboxes_up->size() > 0) {
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_up->begin(), _bboxes_up->end(), vecUp, equal_dist, getSnapperTolerance(), &DistributionSnapper::distUp)) {
+ auto first_dist = distUp(*bbox_to_snap, vecUp.front());
+ Geom::Coord offset = first_dist - equal_dist;
+ Geom::Point target = bbox_to_snap->midpoint() - Geom::Point(0, offset);
+
+ // translate the source bbox to the snap position
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ std::reverse(vecUp.begin(), vecUp.end());
+ vecUp.push_back(bbox);
+
+ _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+
+ if (_bboxes_down->size() > 0) {
+ first_dist = distDown(bbox, _bboxes_down->front());
+ Geom::Coord down_dist;
+ vecDown.clear();
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_down->begin(), _bboxes_down->end(), vecDown, down_dist,
+ getSnapperTolerance(), &DistributionSnapper::distDown)) {
+ if (abs(down_dist - equal_dist) < 1e-4) {
+ vecUp.insert(vecUp.end(), vecDown.begin(), vecDown.end());
+ }
+
+ } else if (abs(first_dist - equal_dist) < 1e-4) {
+ vecUp.insert(vecUp.end(), vecDown.front());
+ }
+ }
+
+ dist_y = abs(offset);
+ sy = SnappedPoint(target, vecUp, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_UP, dist_y, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ snap_y = true;
+ }
+ }
+
+ // 2. if no snaps on top, look Down
+ // if there is a snap then add bottom bboxes and look Up, if there is a snap above then
+ // add those bboxes too
+ if (consider_y && !snap_y && _bboxes_down->size() > 0) {
+ vecDown.clear();
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_down->begin(), _bboxes_down->end(), vecDown, equal_dist, getSnapperTolerance(), &DistributionSnapper::distDown)) {
+ auto first_dist = distDown(*bbox_to_snap, vecDown.front());
+ Geom::Coord offset = first_dist - equal_dist;
+ Geom::Point target = bbox_to_snap->midpoint() + Geom::Point(0, offset);
+
+ // translate the source bbox to the snap position
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ vecDown.insert(vecDown.begin(), bbox);
+
+ _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+
+ if (_bboxes_up->size() > 0) {
+ first_dist = distUp(bbox, _bboxes_up->front());
+ Geom::Coord up_dist;
+ vecUp.clear();
+
+ if (_findSidewaysSnaps(*bbox_to_snap, _bboxes_up->begin(), _bboxes_up->end(), vecUp, up_dist, getSnapperTolerance(), &DistributionSnapper::distUp)) {
+ if (compare_double(up_dist, equal_dist)) {
+ std::reverse(vecUp.begin(), vecUp.end());
+ vecDown.insert(vecDown.begin(), vecUp.begin(), vecUp.end());
+ }
+ } else if (compare_double(first_dist, equal_dist)) {
+ vecDown.insert(vecDown.begin(), vecUp.front());
+ }
+ }
+
+ dist_y = abs(offset);
+ sy = SnappedPoint(target, vecDown, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_DOWN, dist_y, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ snap_y = true;
+ }
+ }
+
+ // 3. if no snap to right or left just add the center snap
+ if (consider_y && !snap_y && vecUp.size() > 0 && vecDown.size() > 0) {
+ auto y = Geom::Point((vecUp.front().max() + vecDown.front().min()) / 2).y();
+ offset = abs(y - bbox_to_snap->midpoint().y());
+ if (consider_y && offset < getSnapperTolerance()) {
+ Geom::Point target = Geom::Point(bbox_to_snap->midpoint().x(), y);
+ // translate the source bbox to the snap position
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ std::vector<Geom::Rect> bboxes = {vecUp.front(), bbox, vecDown.front()};
+
+ _correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+
+ equal_dist = bbox.min().y() - vecUp.front().max().y();
+ sy = SnappedPoint(target, bboxes, bbox, equal_dist, p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_Y, offset, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ snap_y = true;
+ }
+ }
+
+ if (snap_x && snap_y) {
+ Geom::Point target = Geom::Point(sx.getPoint().x(), sy.getPoint().y());
+ Geom::Affine translation = Geom::Translate(target - bbox_to_snap->midpoint());
+ Geom::Rect bbox = *bbox_to_snap * translation;
+ std::vector<Geom::Rect> bboxes_x = sx.getBBoxes();
+ std::vector<Geom::Rect> bboxes_y = sy.getBBoxes();
+
+ // Do not need to correct here, already did that earlier for each direction separately
+ //_correctSelectionBBox(target, p.getPoint(), *bbox_to_snap);
+ auto si = SnappedPoint(target, bboxes_x, bboxes_y, bbox, sx.getDistributionDistance(), sy.getDistributionDistance(), p.getSourceType(), p.getSourceNum(), SNAPTARGET_DISTRIBUTION_XY, offset, getSnapperTolerance(), getSnapperAlwaysSnap(), false, true);
+ isr.points.push_back(si);
+ return;
+ }
+
+ if (snap_x) {
+ isr.points.push_back(sx);
+ }
+
+ if (snap_y) {
+ isr.points.push_back(sy);
+ }
+}
+
+void Inkscape::DistributionSnapper::_correctSelectionBBox(Geom::Point &target,
+ Geom::Point const &p,
+ Geom::Rect const &bbox_to_snap) const
+{
+ if (_snapmanager->_desktop->selection->size() > 1) {
+ auto correction = bbox_to_snap.midpoint() - p;
+ target -= correction;
+ }
+}
+
+void Inkscape::DistributionSnapper::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
+{
+ if (bbox_to_snap.empty())
+ return;
+
+ if (!(p.getSourceType() & SNAPSOURCE_BBOX_CATEGORY)) {
+ return;
+ }
+
+ // toggle checks
+ if (!_snap_enabled || !_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_DISTRIBUTION_CATEGORY))
+ return;
+
+ if (p.getSourceNum() <= 0) {
+ Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint());
+ _snapmanager->_findCandidates(_snapmanager->getDocument()->getRoot(), it, local_bbox_to_snap, false, Geom::identity());
+ }
+
+ _snapEquidistantPoints(isr, p, bbox_to_snap, unselected_nodes);
+}
+
+void Inkscape::DistributionSnapper::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
+{
+ if (bbox_to_snap.empty())
+ return;
+
+ // toggle checks
+ if (!_snap_enabled || !_snapmanager->snapprefs.isTargetSnappable(SNAPTARGET_DISTRIBUTION_CATEGORY))
+ return;
+
+ // project the mouse pointer onto the constraint. Only the projected point will be considered for snapping
+ Geom::Point pp = c.projection(p.getPoint());
+
+ if (p.getSourceNum() <= 0) {
+ Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p.getPoint(), p.getPoint());
+ _snapmanager->_findCandidates(_snapmanager->getDocument()->getRoot(), it, local_bbox_to_snap, false, Geom::identity());
+ }
+
+ _snapEquidistantPoints(isr, p, bbox_to_snap, unselected_nodes, c, pp);
+}
+
+bool Inkscape::DistributionSnapper::ThisSnapperMightSnap() const
+{
+ return true;
+}
+
+bool Inkscape::DistributionSnapper::getSnapperAlwaysSnap() const
+{
+ // TODO: Replace this threshold of 10000 by a constant; see also tolerance-slider.cpp
+ return _snapmanager->snapprefs.getDistributionTolerance() == 10000;
+}
+
+Geom::Coord Inkscape::DistributionSnapper::getSnapperTolerance() const
+{
+ SPDesktop const *dt = _snapmanager->getDesktop();
+ double const zoom = dt ? dt->current_zoom() : 1;
+ return _snapmanager->snapprefs.getDistributionTolerance() / zoom;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :