diff --git a/src/snap.cpp b/src/snap.cpp
index 3cac53ebe6d74eeb90dac9b01ccb5d93f28c126c..8704ce3bb69200752fed6dba11088f10523053dc 100644 (file)
--- a/src/snap.cpp
+++ b/src/snap.cpp
if (s.getSnapped()) {
_desktop->snapindicator->set_new_snaptarget(s, true);
} else {
- _desktop->snapindicator->remove_snaptarget();
+ _desktop->snapindicator->remove_snaptarget(true);
}
_snapindicator = true; // restore the original value
}
* \return Offset vector after snapping to the closest multiple of a grid pitch
*/
-Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t) const
+Geom::Point SnapManager::multipleOfGridPitch(Geom::Point const &t, Geom::Point const &origin)
{
- if (!snapprefs.getSnapEnabledGlobally()) // No need to check for snapprefs.getSnapPostponedGlobally() here
+ if (!snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally())
return t;
if (_desktop && _desktop->gridsEnabled()) {
bool success = false;
Geom::Point nearest_multiple;
Geom::Coord nearest_distance = NR_HUGE;
+ Inkscape::SnappedPoint bestSnappedPoint(t);
// It will snap to the grid for which we find the closest snap. This might be a different
// grid than to which the objects were initially aligned. I don't see an easy way to fix
Geom::Point const t_offset = t + grid->origin;
SnappedConstraints sc;
// Only the first three parameters are being used for grid snappers
- snapper->freeSnap(sc, Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_UNDEFINED),Geom::OptRect(), NULL, NULL);
+ snapper->freeSnap(sc, Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH),Geom::OptRect(), NULL, NULL);
// Find the best snap for this grid, including intersections of the grid-lines
- Inkscape::SnappedPoint s = findBestSnap(Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_UNDEFINED), sc, false);
+ bool old_val = _snapindicator;
+ _snapindicator = false;
+ Inkscape::SnappedPoint s = findBestSnap(Inkscape::SnapCandidatePoint(t_offset, Inkscape::SNAPSOURCE_GRID_PITCH), sc, false, false, true);
+ _snapindicator = old_val;
if (s.getSnapped() && (s.getSnapDistance() < nearest_distance)) {
// use getSnapDistance() instead of getWeightedDistance() here because the pointer's position
// doesn't tell us anything about which node to snap
success = true;
nearest_multiple = s.getPoint() - to_2geom(grid->origin);
nearest_distance = s.getSnapDistance();
+ bestSnappedPoint = s;
}
}
}
- if (success)
+ if (success) {
+ bestSnappedPoint.setPoint(origin + nearest_multiple);
+ _desktop->snapindicator->set_new_snaptarget(bestSnappedPoint);
return nearest_multiple;
+ }
}
return t;
@@ -357,21 +365,29 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::SnapCandidatePoint
// First project the mouse pointer onto the constraint
Geom::Point pp = constraint.projection(p.getPoint());
+ Inkscape::SnappedPoint no_snap = Inkscape::SnappedPoint(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_CONSTRAINT, Geom::L2(pp - p.getPoint()), 0, false, false);
+
if (!someSnapperMightSnap()) {
- // The constraint should always be enforce, so we return pp here instead of p
- return Inkscape::SnappedPoint(pp, Inkscape::SNAPTARGET_UNDEFINED, NR_HUGE, 0, false, false);
+ // Always return point on constraint
+ return no_snap;
}
- // Then try to snap the projected point
- Inkscape::SnapCandidatePoint candidate(pp, p.getSourceType(), p.getSourceNum(), Inkscape::SNAPTARGET_UNDEFINED, Geom::Rect());
-
SnappedConstraints sc;
SnapperList const snappers = getSnappers();
for (SnapperList::const_iterator i = snappers.begin(); i != snappers.end(); i++) {
- (*i)->constrainedSnap(sc, candidate, bbox_to_snap, constraint, &_items_to_ignore);
+ (*i)->constrainedSnap(sc, p, bbox_to_snap, constraint, &_items_to_ignore);
}
- return findBestSnap(candidate, sc, true);
+ Inkscape::SnappedPoint result = findBestSnap(p, sc, true);
+
+ if (result.getSnapped()) {
+ // only change the snap indicator if we really snapped to something
+ if (_snapindicator) {
+ _desktop->snapindicator->set_new_snaptarget(result);
+ }
+ return result;
+ }
+ return no_snap;
}
/**
/* Quick check to see if we have any snappers that are enabled
** Also used to globally disable all snapping
*/
- if (someSnapperMightSnap() == false) {
+ if (someSnapperMightSnap() == false || points.size() == 0) {
return Inkscape::SnappedPoint(pointer);
}
g_assert(best_snapped_point.getAlwaysSnap() == false); // Check initialization of snapped point
g_assert(best_snapped_point.getAtIntersection() == false);
- std::vector<Inkscape::SnapCandidatePoint>::const_iterator j = transformed_points.begin();
+ std::vector<Inkscape::SnapCandidatePoint>::iterator j = transformed_points.begin();
// std::cout << std::endl;
+ bool first_free_snap = true;
for (std::vector<Inkscape::SnapCandidatePoint>::const_iterator i = points.begin(); i != points.end(); i++) {
/* Snap it */
dedicated_constraint = Inkscape::Snapper::ConstraintLine(origin, component_vectors[c1]);
snapped_point = constrainedSnap(*j, dedicated_constraint, bbox);
} else {
+ // If we have a collection of SnapCandidatePoints, with mixed constrained snapping and free snapping
+ // requirements, then freeSnap might never see the SnapCandidatePoint with source_num == 0. The freeSnap()
+ // method in the object snapper depends on this, because only for source-num == 0 the target nodes will
+ // be collected. Therefore we enforce that the first SnapCandidatePoint that is to be freeSnapped always
+ // has source_num == 0;
+ // TODO: This is a bit ugly so fix this; do we need sourcenum for anything else? if we don't then get rid
+ // of it and explicitely communicate to the object snapper that this is a first point
+ if (first_free_snap) {
+ (*j).setSourceNum(0);
+ first_free_snap = false;
+ }
snapped_point = freeSnap(*j, bbox);
}
}
@@ -875,15 +903,18 @@ Inkscape::SnappedPoint SnapManager::constrainedSnapSkew(std::vector<Inkscape::Sn
* \param sc A structure holding all snap targets that have been found so far
* \param constrained True if the snap is constrained, e.g. for stretching or for purely horizontal translation.
* \param noCurves If true, then do consider snapping to intersections of curves, but not to the curves themselves
+ * \param allowOffScreen If true, then snapping to points which are off the screen is allowed (needed for example when pasting to the grid)
* \return An instance of the SnappedPoint class, which holds data on the snap source, snap target, and various metrics
*/
Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint const &p,
SnappedConstraints const &sc,
bool constrained,
- bool noCurves) const
+ bool noCurves,
+ bool allowOffScreen) const
{
+
/*
std::cout << "Type and number of snapped constraints: " << std::endl;
std::cout << " Points : " << sc.points.size() << std::endl;
@@ -969,13 +1000,15 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(Inkscape::SnapCandidatePoint co
Inkscape::SnappedPoint bestSnappedPoint(p.getPoint());
// std::cout << "Finding the best snap..." << std::endl;
for (std::list<Inkscape::SnappedPoint>::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) {
- // first find out if this snapped point is within snapping range
// std::cout << "sp = " << (*i).getPoint() << " | source = " << (*i).getSource() << " | target = " << (*i).getTarget();
- if ((*i).getSnapDistance() <= (*i).getTolerance()) {
- // if it's the first point, or if it is closer than the best snapped point so far
- if (i == sp_list.begin() || bestSnappedPoint.isOtherSnapBetter(*i, false)) {
- // then prefer this point over the previous one
- bestSnappedPoint = *i;
+ bool onScreen = _desktop->get_display_area().contains((*i).getPoint());
+ if (onScreen || allowOffScreen) { // Only snap to points which are not off the screen
+ if ((*i).getSnapDistance() <= (*i).getTolerance()) { // Only snap to points within snapping range
+ // if it's the first point, or if it is closer than the best snapped point so far
+ if (i == sp_list.begin() || bestSnappedPoint.isOtherSnapBetter(*i, false)) {
+ // then prefer this point over the previous one
+ bestSnappedPoint = *i;
+ }
}
}
// std::cout << std::endl;