diff --git a/src/seltrans.cpp b/src/seltrans.cpp
index 0b1c4f5c97bafafccbc7437dcf4ffa8dfc392317..e55c25d797a2ce1fc643394a87b06f7a0e21c62a 100644 (file)
--- a/src/seltrans.cpp
+++ b/src/seltrans.cpp
@@ -285,16 +285,20 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
// Next, get all points to consider for snapping
SnapManager const &m = _desktop->namedview->snap_manager;
+ Inkscape::SnapPreferences local_snapprefs = m.snapprefs;
+ local_snapprefs.setSnapToItemNode(true); // We should get at least the cusp nodes here. This might
+ // have been turned off because (for example) the user only want paths as a snap target, not nodes
+ // but as a snap source we still need some nodes though!
_snap_points.clear();
- _snap_points = selection->getSnapPoints(&m.snapprefs);
- std::vector<Geom::Point> snap_points_hull = selection->getSnapPointsConvexHull(&m.snapprefs);
- if (_snap_points.size() > 100) {
- /* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes
- An average user would rarely ever try to snap such a large number of nodes anyway, because
- (s)he could hardly discern which node would be snapping */
- _snap_points = snap_points_hull;
- // Unfortunately, by now we will have lost the font-baseline snappoints :-(
- }
+ _snap_points = selection->getSnapPoints(&local_snapprefs);
+ std::vector<Geom::Point> snap_points_hull = selection->getSnapPointsConvexHull(&local_snapprefs);
+ if (_snap_points.size() > 100) {
+ /* Snapping a huge number of nodes will take way too long, so limit the number of snappable nodes
+ An average user would rarely ever try to snap such a large number of nodes anyway, because
+ (s)he could hardly discern which node would be snapping */
+ _snap_points = snap_points_hull;
+ // Unfortunately, by now we will have lost the font-baseline snappoints :-(
+ }
// Find bbox hulling all special points, which excludes stroke width. Here we need to include the
// path nodes, for example because a rectangle which has been converted to a path doesn't have
@@ -312,11 +316,10 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
_bbox_points.clear();
if (_bbox) {
- // ... and add the bbox corners to _bbox_points
- for ( unsigned i = 0 ; i < 4 ; i++ ) {
- _bbox_points.push_back(_bbox->corner(i));
- }
- // There are two separate "opposites" (i.e. opposite w.r.t. the handle being dragged):
+ if (m.snapprefs.getSnapModeBBox()) {
+ getBBoxPoints(_bbox, &_bbox_points, true, m.snapprefs.getSnapBBoxEdgeMidpoints(), m.snapprefs.getSnapBBoxMidpoints());
+ }
+ // There are two separate "opposites" (i.e. opposite w.r.t. the handle being dragged):
// - one for snapping the boundingbox, which can be either visual or geometric
// - one for snapping the special points
// The "opposite" in case of a geometric boundingbox always coincides with the "opposite" for the special points
@@ -327,7 +330,47 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s
_opposite = _opposite_for_bboxpoints;
}
- // The lines below are usefull for debugging any snapping issues, as they'll spit out all points that are considered for snapping
+ // When snapping the node closest to the mouse pointer is absolutely preferred over the closest snap
+ // (i.e. when weight == 1), then we will not even try to snap to other points and discard those other
+ // points immediately.
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/options/snapclosestonly/value", false)) {
+ if (m.snapprefs.getSnapModeNode()) {
+ _keepClosestPointOnly(_snap_points, p);
+ } else {
+ _snap_points.clear(); // don't keep any point
+ }
+
+ if (m.snapprefs.getSnapModeBBox()) {
+ _keepClosestPointOnly(_bbox_points, p);
+ } else {
+ _bbox_points.clear(); // don't keep any point
+ }
+
+ g_assert(_bbox_points.size() < 2 && _snap_points.size() < 2);
+ if (_snap_points.size() == 1 && _bbox_points.size() == 1) { //both vectors can only have either one or zero elements
+ // So we have exactly one bbox corner and one node left; now find out which is closest and delete the other one
+ if (Geom::L2(_snap_points.at(0) - p) < Geom::L2(_bbox_points.at(0) - p)) {
+ _bbox_points.clear();
+ } else {
+ _snap_points.clear();
+ }
+ }
+
+ // Now either _bbox_points or _snap_points has a single element, the other one has zero..... or both have zero elements
+ g_assert((_bbox_points.size() + _snap_points.size()) < 2);
+ if (m.snapprefs.getSnapEnabledGlobally()) {
+ if (_bbox_points.size() == 1) {
+ _desktop->snapindicator->set_new_snapsource(_bbox_points.at(0));
+ } else if (_snap_points.size() == 1){
+ _desktop->snapindicator->set_new_snapsource(_snap_points.at(0));
+ }
+ }
+ }
+
+ sp_canvas_set_snap_delay_active(_desktop->canvas, true);
+
+ // The lines below are useful for debugging any snapping issues, as they'll spit out all points that are considered for snapping
/*std::cout << "Number of snap points: " << _snap_points.size() << std::endl;
for (std::vector<Geom::Point>::const_iterator i = _snap_points.begin(); i != _snap_points.end(); i++)
_grabbed = false;
_show_handles = true;
+ sp_canvas_set_snap_delay_active(_desktop->canvas, false);
+
+ _desktop->snapindicator->remove_snapsource();
+
Inkscape::Selection *selection = sp_desktop_selection(_desktop);
_updateVolatileState();
}
// Snap along a suitable constraint vector from the origin.
- bb = m.constrainedSnapScale(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, default_scale, _origin_for_bboxpoints);
- sn = m.constrainedSnapScale(SnapPreferences::SNAPPOINT_NODE, _snap_points, geom_scale, _origin_for_specpoints);
+ bb = m.constrainedSnapScale(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, default_scale, _origin_for_bboxpoints);
+ sn = m.constrainedSnapScale(SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, geom_scale, _origin_for_specpoints);
/* Choose the smaller difference in scale. Since s[X] == s[Y] we can
** just compare difference in s[X].
sd = sn.getSnapped() ? fabs(sn.getTransformation()[Geom::X] - geom_scale[Geom::X]) : NR_HUGE;
} else {
/* Scale aspect ratio is unlocked */
- bb = m.freeSnapScale(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, default_scale, _origin_for_bboxpoints);
- sn = m.freeSnapScale(SnapPreferences::SNAPPOINT_NODE, _snap_points, geom_scale, _origin_for_specpoints);
+ bb = m.freeSnapScale(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, default_scale, _origin_for_bboxpoints);
+ sn = m.freeSnapScale(SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, geom_scale, _origin_for_specpoints);
/* Pick the snap that puts us closest to the original scale */
bd = bb.getSnapped() ? fabs(Geom::L2(bb.getTransformation()) - Geom::L2(Geom::Point(default_scale[Geom::X], default_scale[Geom::Y]))) : NR_HUGE;
if (!(bb.getSnapped() || sn.getSnapped())) {
// We didn't snap at all! Don't update the handle position, just calculate the new transformation
_calcAbsAffineDefault(default_scale);
- _desktop->snapindicator->remove_snappoint();
+ _desktop->snapindicator->remove_snaptarget();
} else if (bd < sd) {
// We snapped the bbox (which is either visual or geometric)
- _desktop->snapindicator->set_new_snappoint(bb);
+ _desktop->snapindicator->set_new_snaptarget(bb);
default_scale = Geom::Scale(bb.getTransformation());
// Calculate the new transformation and update the handle position
pt = _calcAbsAffineDefault(default_scale);
} else {
- _desktop->snapindicator->set_new_snappoint(sn);
+ _desktop->snapindicator->set_new_snaptarget(sn);
// We snapped the special points (e.g. nodes), which are not at the visual bbox
// The handle location however (pt) might however be at the visual bbox, so we
// will have to calculate pt taking the stroke width into account
@@ -1031,8 +1078,8 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, Geom
bool symmetrical = state & GDK_CONTROL_MASK;
- bb = m.constrainedSnapStretch(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, Geom::Coord(default_scale[axis]), _origin_for_bboxpoints, Geom::Dim2(axis), symmetrical);
- sn = m.constrainedSnapStretch(SnapPreferences::SNAPPOINT_NODE, _snap_points, Geom::Coord(geom_scale[axis]), _origin_for_specpoints, Geom::Dim2(axis), symmetrical);
+ bb = m.constrainedSnapStretch(SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, Geom::Coord(default_scale[axis]), _origin_for_bboxpoints, Geom::Dim2(axis), symmetrical);
+ sn = m.constrainedSnapStretch(SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, Geom::Coord(geom_scale[axis]), _origin_for_specpoints, Geom::Dim2(axis), symmetrical);
if (bb.getSnapped()) {
// We snapped the bbox (which is either visual or geometric)
@@ -1055,13 +1102,13 @@ gboolean Inkscape::SelTrans::stretchRequest(SPSelTransHandle const &handle, Geom
if (!(bb.getSnapped() || sn.getSnapped())) {
// We didn't snap at all! Don't update the handle position, just calculate the new transformation
_calcAbsAffineDefault(default_scale);
- _desktop->snapindicator->remove_snappoint();
+ _desktop->snapindicator->remove_snaptarget();
} else if (bd < sd) {
- _desktop->snapindicator->set_new_snappoint(bb);
+ _desktop->snapindicator->set_new_snaptarget(bb);
// Calculate the new transformation and update the handle position
pt = _calcAbsAffineDefault(default_scale);
} else {
- _desktop->snapindicator->set_new_snappoint(sn);
+ _desktop->snapindicator->set_new_snaptarget(sn);
// We snapped the special points (e.g. nodes), which are not at the visual bbox
// The handle location however (pt) might however be at the visual bbox, so we
// will have to calculate pt taking the stroke width into account
@@ -1149,15 +1196,15 @@ gboolean Inkscape::SelTrans::skewRequest(SPSelTransHandle const &handle, Geom::P
Inkscape::Snapper::ConstraintLine const constraint(component_vectors[dim_b]);
// When skewing, we cannot snap the corners of the bounding box, see the comment in "constrainedSnapSkew" for details
Geom::Point const s(skew[dim_a], scale[dim_a]);
- Inkscape::SnappedPoint sn = m.constrainedSnapSkew(Inkscape::SnapPreferences::SNAPPOINT_NODE, _snap_points, constraint, s, _origin, Geom::Dim2(dim_b));
+ Inkscape::SnappedPoint sn = m.constrainedSnapSkew(Inkscape::SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, constraint, s, _origin, Geom::Dim2(dim_b));
if (sn.getSnapped()) {
// We snapped something, so change the skew to reflect it
Geom::Coord const sd = sn.getSnapped() ? sn.getTransformation()[0] : NR_HUGE;
- _desktop->snapindicator->set_new_snappoint(sn);
+ _desktop->snapindicator->set_new_snaptarget(sn);
skew[dim_a] = sd;
} else {
- _desktop->snapindicator->remove_snappoint();
+ _desktop->snapindicator->remove_snaptarget();
}
}
// individually for each point to be snapped; this will be handled however by _snapTransformed()
s.push_back(m.constrainedSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_BBOX,
_bbox_points,
+ _point,
Inkscape::Snapper::ConstraintLine(component_vectors[dim]),
dxy));
s.push_back(m.constrainedSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_NODE,
_snap_points,
+ _point,
Inkscape::Snapper::ConstraintLine(component_vectors[dim]),
dxy));
}
g_get_current_time(&starttime); */
/* Snap to things with no constraint */
- s.push_back(m.freeSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_BBOX, _bbox_points, dxy));
- s.push_back(m.freeSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_NODE, _snap_points, dxy));
+ s.push_back(m.freeSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_BBOX, _bbox_points, _point, dxy));
+ s.push_back(m.freeSnapTranslation(Inkscape::SnapPreferences::SNAPPOINT_NODE, _snap_points, _point, dxy));
/*g_get_current_time(&endtime);
double elapsed = ((((double)endtime.tv_sec - starttime.tv_sec) * G_USEC_PER_SEC + (endtime.tv_usec - starttime.tv_usec))) / 1000.0;
/* Pick one */
Inkscape::SnappedPoint best_snapped_point;
- g_assert(best_snapped_point.getDistance() == NR_HUGE);
for (std::list<Inkscape::SnappedPoint>::const_iterator i = s.begin(); i != s.end(); i++) {
if (i->getSnapped()) {
- if (i->getDistance() < best_snapped_point.getDistance()) {
+ if (best_snapped_point.isOtherSnapBetter(*i, true)) {
best_snapped_point = *i;
dxy = i->getTransformation();
}
}
}
if (best_snapped_point.getSnapped()) {
- _desktop->snapindicator->set_new_snappoint(best_snapped_point);
+ _desktop->snapindicator->set_new_snaptarget(best_snapped_point);
} else {
- // We didn't snap, so remove any previous snap indicator
- _desktop->snapindicator->remove_snappoint();
+ // We didn't snap, so remove any previous snap indicator
+ _desktop->snapindicator->remove_snaptarget();
if (control) {
// If we didn't snap, then we should still constrain horizontally or vertically
// (When we did snap, then this constraint has already been enforced by
}
}
}
-
+
Geom::Matrix const move((Geom::Translate(dxy)));
Geom::Point const norm(0, 0);
transform(move, norm);
@@ -1543,6 +1591,25 @@ Geom::Point Inkscape::SelTrans::_calcAbsAffineGeom(Geom::Scale const geom_scale)
return visual_bbox.min() + visual_bbox.dimensions() * Geom::Scale(_handle_x, _handle_y);
}
+void Inkscape::SelTrans::_keepClosestPointOnly(std::vector<Geom::Point> &points, const Geom::Point &reference)
+{
+ if (points.size() < 2) return;
+
+ Geom::Point closest_point = Geom::Point(NR_HUGE, NR_HUGE);
+ Geom::Coord closest_dist = NR_HUGE;
+
+ for(std::vector<Geom::Point>::const_iterator i = points.begin(); i != points.end(); i++) {
+ Geom::Coord dist = Geom::L2(*i - reference);
+ if (i == points.begin() || dist < closest_dist) {
+ closest_point = *i;
+ closest_dist = dist;
+ }
+ }
+
+ points.clear();
+ points.push_back(closest_point);
+}
+
/*
Local Variables:
mode:c++