summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 5994f4e)
raw | patch | inline | side by side (parent: 5994f4e)
author | dvlierop2 <dvlierop2@users.sourceforge.net> | |
Thu, 3 Jan 2008 21:37:41 +0000 (21:37 +0000) | ||
committer | dvlierop2 <dvlierop2@users.sourceforge.net> | |
Thu, 3 Jan 2008 21:37:41 +0000 (21:37 +0000) |
2) fix constrained snapping
3) improve snapping logic (again), or more specifically: better obey "always snap"
4) refactoring to reduce risk of bugs: renaming variables and methods for better readability, adding class members instead of using std::pairs, etc.
3) improve snapping logic (again), or more specifically: better obey "always snap"
4) refactoring to reduce risk of bugs: renaming variables and methods for better readability, adding class members instead of using std::pairs, etc.
13 files changed:
diff --git a/src/desktop.cpp b/src/desktop.cpp
index feb3f5cb0287ece3a51a8a05af074332473f98c4..e52d2b50c2a7c1f95e2c669e8a53456a36d3111b 100644 (file)
--- a/src/desktop.cpp
+++ b/src/desktop.cpp
//tell all grid snappers
for ( GSList const *l = nv.grids; l != NULL; l = l->next) {
Inkscape::CanvasGrid *grid = (Inkscape::CanvasGrid*) l->data;
- grid->snapper->setDistance(sp_convert_distance_full(nv.gridtolerance,
+ grid->snapper->setSnapperTolerance(sp_convert_distance_full(nv.gridtolerance,
*nv.gridtoleranceunit,
px));
}
- nv.snap_manager.guide.setDistance(sp_convert_distance_full(nv.guidetolerance,
+ nv.snap_manager.guide.setSnapperTolerance(sp_convert_distance_full(nv.guidetolerance,
*nv.guidetoleranceunit,
px));
- nv.snap_manager.object.setDistance(sp_convert_distance_full(nv.objecttolerance,
+ nv.snap_manager.object.setSnapperTolerance(sp_convert_distance_full(nv.objecttolerance,
*nv.objecttoleranceunit,
px));
}
index 3d9375d2d1f0001538d57cc9c89c119e218341a1..c64887d1c91e104faf19e79b3778f55345c1d150 100644 (file)
void CanvasAxonomGridSnapper::_addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, NR::Point const point_on_line) const
{
- SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, normal_to_line, point_on_line);
+ SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
sc.grid_lines.push_back(dummy);
}
index 15a6452d3c94765cf295522b62c63be847ffbf85..20856709cfdfcd3251004f1ed1ec597289724cfa 100644 (file)
void CanvasXYGridSnapper::_addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, NR::Point const point_on_line) const
{
- SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, normal_to_line, point_on_line);
+ SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
sc.grid_lines.push_back(dummy);
}
diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp
index fb16382d26eddc2862e6171be9d895e286991065..41e2cdb0708bc480366cda9317277bb9570ec661 100644 (file)
--- a/src/guide-snapper.cpp
+++ b/src/guide-snapper.cpp
void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, NR::Point const snapped_point, NR::Coord const snapped_distance, NR::Point const normal_to_line, NR::Point const point_on_line) const
{
- SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, normal_to_line, point_on_line);
+ SnappedLine dummy = SnappedLine(snapped_point, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), normal_to_line, point_on_line);
sc.guide_lines.push_back(dummy);
}
diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp
index 7fc6a32a473db511b5c3e49a2c82a10d628010a2..a84a802961fe04ed40a6bd9411bcb9e51bb637d5 100644 (file)
--- a/src/line-snapper.cpp
+++ b/src/line-snapper.cpp
std::vector<NR::Point> &points_to_snap,
std::list<SPItem const *> const &it) const
{
- Inkscape::SnappedPoint s = SnappedPoint(p, NR_HUGE);
-
/* Get the lines that we will try to snap to */
const LineList lines = _getSnapLines(p);
NR::Point const p_proj = project_on_linesegment(p, p1, p2);
NR::Coord const dist = NR::L2(p_proj - p);
//Store any line that's within snapping range
- if (dist < getDistance()) {
+ if (dist < getSnapperTolerance()) {
_addSnappedLine(sc, p_proj, dist, i->first, i->second);
// std::cout << " -> distance = " << dist;
}
std::list<SPItem const *> const &/*it*/) const
{
- Inkscape::SnappedPoint s = SnappedPoint(p, NR_HUGE);
-
/* Get the lines that we will try to snap to */
const LineList lines = _getSnapLines(p);
for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) {
-
- /* Normal to the line we're trying to snap along */
- NR::Point const n(NR::rot90(NR::unit_vector(c.getDirection())));
-
- NR::Point const point_on_line = c.hasPoint() ? c.getPoint() : p;
-
- /* Constant term of the line we're trying to snap along */
- NR::Coord const q0 = dot(n, point_on_line);
- /* Constant term of the grid or guide line */
- NR::Coord const q1 = dot(i->first, i->second);
-
- /* Try to intersect this line with the target line */
- Geom::Point t_2geom(NR_HUGE, NR_HUGE);
- Geom::IntersectorKind const k = Geom::line_intersection(n.to_2geom(), q0, i->first.to_2geom(), q1, t_2geom);
- NR::Point t(t_2geom);
-
- if (k == Geom::intersects) {
- const NR::Coord dist = L2(t - p);
- if (dist < getDistance()) {
- // When doing a constrained snap, we're already at an intersection.
- // This snappoint is therefore fully constrained, so there's no need
- // to look for additional intersections; just return the snapped point
- // and forget about the line
- sc.points.push_back(SnappedPoint(t, dist));
+ if (NR::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint
+ /* Normal to the line we're trying to snap along */
+ NR::Point const n(NR::rot90(NR::unit_vector(c.getDirection())));
+
+ NR::Point const point_on_line = c.hasPoint() ? c.getPoint() : p;
+
+ /* Constant term of the line we're trying to snap along */
+ NR::Coord const q0 = dot(n, point_on_line);
+ /* Constant term of the grid or guide line */
+ NR::Coord const q1 = dot(i->first, i->second);
+
+ /* Try to intersect this line with the target line */
+ Geom::Point t_2geom(NR_HUGE, NR_HUGE);
+ Geom::IntersectorKind const k = Geom::line_intersection(n.to_2geom(), q0, i->first.to_2geom(), q1, t_2geom);
+ NR::Point t(t_2geom);
+
+ if (k == Geom::intersects) {
+ const NR::Coord dist = L2(t - p);
+ if (dist < getSnapperTolerance()) {
+ // When doing a constrained snap, we're already at an intersection.
+ // This snappoint is therefore fully constrained, so there's no need
+ // to look for additional intersections; just return the snapped point
+ // and forget about the line
+ sc.points.push_back(SnappedPoint(t, dist, getSnapperTolerance(), getSnapperAlwaysSnap()));
+ }
}
}
}
diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp
index c03884ffd05880a14a89e6981262c1232aea2ae3..9ffdc764cbde7a79358143911758f764591f052e 100644 (file)
--- a/src/object-snapper.cpp
+++ b/src/object-snapper.cpp
for (std::vector<NR::Point>::const_iterator i = points_to_snap.begin(); i != points_to_snap.end(); i++) {
NR::Point b_min = b->min();
NR::Point b_max = b->max();
- double d = getDistance();
- bool withinX = ((*i)[NR::X] >= b_min[NR::X] - d) && ((*i)[NR::X] <= b_max[NR::X] + d);
- bool withinY = ((*i)[NR::Y] >= b_min[NR::Y] - d) && ((*i)[NR::Y] <= b_max[NR::Y] + d);
+ NR::Coord t = getSnapperTolerance();
+ bool withinX = ((*i)[NR::X] >= b_min[NR::X] - t) && ((*i)[NR::X] <= b_max[NR::X] + t);
+ bool withinY = ((*i)[NR::Y] >= b_min[NR::Y] - t) && ((*i)[NR::Y] <= b_max[NR::Y] + t);
bool c1 = snap_dim == GUIDE_TRANSL_SNAP_X && withinX;
bool c2 = snap_dim == GUIDE_TRANSL_SNAP_Y && withinY;
bool c3 = snap_dim == TRANSL_SNAP_XY && withinX && withinY;
for (std::vector<NR::Point>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) {
NR::Coord dist = NR::L2(*k - p);
- if (dist < getDistance() && dist < s.getDistance()) {
- s = SnappedPoint(*k, dist);
+ if (dist < getSnapperTolerance() && dist < s.getDistance()) {
+ s = SnappedPoint(*k, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
success = true;
}
}
@@ -217,8 +217,8 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s
// Project each node (*k) on the guide line (running through point p)
NR::Point p_proj = project_on_linesegment(*k, p, p + NR::rot90(guide_normal));
NR::Coord dist = NR::L2(*k - p_proj);
- if (dist < getDistance() && dist < s.getDistance()) {
- s = SnappedPoint(*k, dist);
+ if (dist < getSnapperTolerance() && dist < s.getDistance()) {
+ s = SnappedPoint(*k, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
success = true;
}
}
NR::Point const o_dt = desktop->doc2dt(o_it);
NR::Coord const dist = NR::L2(o_dt - p);
- if (dist < getDistance()) {
+ if (dist < getSnapperTolerance()) {
// if we snap to a straight line segment (within a path), then return this line segment
if ((*k)->IsLineSegment(o->piece)) {
NR::Point start_point;
(*k)->PointAt(o->piece, 1, end_point);
start_point = desktop->doc2dt(start_point);
end_point = desktop->doc2dt(end_point);
- sc.lines.push_back(Inkscape::SnappedLineSegment(o_dt, dist, start_point, end_point));
+ sc.lines.push_back(Inkscape::SnappedLineSegment(o_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), start_point, end_point));
} else {
// for segments other than straight lines of a path, we'll return just the closest snapped point
if (dist < s.getDistance()) {
- s = SnappedPoint(o_dt, dist);
+ s = SnappedPoint(o_dt, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
success = true;
}
}
// The intersection point of the constraint line with any path,
// must lie within two points on the constraintline: p_min_on_cl and p_max_on_cl
- // The distance between those points is twice the max. snapping distance
+ // The distance between those points is twice the snapping tolerance
NR::Point const p_proj_on_cl = project_on_linesegment(p, p1_on_cl, p2_on_cl);
- NR::Point const p_min_on_cl = desktop->dt2doc(p_proj_on_cl - getDistance() * direction_vector);
- NR::Point const p_max_on_cl = desktop->dt2doc(p_proj_on_cl + getDistance() * direction_vector);
+ NR::Point const p_min_on_cl = desktop->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector);
+ NR::Point const p_max_on_cl = desktop->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector);
Geom::Path cl;
cl.start(p_min_on_cl.to_2geom());
for (std::vector<NArtBpath*>::const_iterator k = _bpaths_to_snap_to->begin(); k != _bpaths_to_snap_to->end(); k++) {
if (*k) {
// convert a Path object (see src/livarot/Path.h) to a 2geom's path object (see 2geom/path.h)
- // TODO (Diederik) Only do this once for the first point, needs some storage of pointers in a member variable
+ // TODO: (Diederik) Only do this once for the first point, needs some storage of pointers in a member variable
std::vector<Geom::Path> path_2geom = BPath_to_2GeomPath(*k);
for (std::vector<Geom::Path>::const_iterator l = path_2geom.begin(); l != path_2geom.end(); l++) {
// When it's within snapping range, then return it
// (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < tb < 1)
if ((*m).tb >= 0 && (*m).tb <= 1 ) {
- SnappedPoint s(desktop->doc2dt(p_inters), NR::L2(p_proj_on_cl - p_inters));
+ NR::Coord dist = NR::L2(desktop->dt2doc(p_proj_on_cl) - p_inters);
+ SnappedPoint s(desktop->doc2dt(p_inters), dist, getSnapperTolerance(), getSnapperAlwaysSnap());
sc.points.push_back(s);
}
}
diff --git a/src/snap.cpp b/src/snap.cpp
index 62449f37dcedab68906bb5d4519c1cc1362bcc3f..a6e165d5030b14d88a4a429e9722186e1fb72f5f 100644 (file)
--- a/src/snap.cpp
+++ b/src/snap.cpp
std::list<SPItem const *> const &it) const
{
if (!SomeSnapperMightSnap()) {
- return Inkscape::SnappedPoint(p, NR_HUGE);
+ return Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
}
SnappedConstraints sc;
@@ -271,7 +271,7 @@ Inkscape::SnappedPoint SnapManager::constrainedSnap(Inkscape::Snapper::PointType
std::list<SPItem const *> const &it) const
{
if (!SomeSnapperMightSnap()) {
- return Inkscape::SnappedPoint(p, NR_HUGE);
+ return Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
}
SnappedConstraints sc;
// This method is used to snap a guide to nodes, while dragging the guide around
if (!(object.ThisSnapperMightSnap() && _snap_enabled_globally)) {
- return Inkscape::SnappedPoint(p, NR_HUGE);
+ return Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
}
SnappedConstraints sc;
*/
NR::Coord best_metric = NR_HUGE;
NR::Coord best_second_metric = NR_HUGE;
+ NR::Point best_scale_metric(NR_HUGE, NR_HUGE);
bool best_at_intersection = false;
+ bool best_always_snap = false;
std::vector<NR::Point>::const_iterator j = transformed_points.begin();
NR::Point result;
NR::Coord metric = NR_HUGE;
NR::Coord second_metric = NR_HUGE;
+ NR::Point scale_metric(NR_HUGE, NR_HUGE);
if (snapped.getDistance() < NR_HUGE) {
/* We snapped. Find the transformation that describes where the snapped point has
break;
case SCALE:
{
- NR::Point const a = (snapped.getPoint() - origin);
- NR::Point const b = (*i - origin);
- result = transformation;
- if (fabs(b[NR::X]) > 1e-6 && fabs(b[NR::Y]) > 1e-6) {
- // This is the default scaling that results after snapping
- result = NR::Point(a[NR::X] / b[NR::X], a[NR::Y] / b[NR::Y]);
- } else {
- // If this point *i is horizontally or vertically aligned with
- // the origin of the scaling, then it will scale purely in X or Y
- // We can therefore only calculate the scaling in this direction
- // and the scaling factor for the other direction should remain
- // untouched (unless scaling is uniform ofcourse)
- for (int index = 0; index < 2; index++) {
- if (fabs(b[index]) > 1e-6) {
- result[index] = a[index] / b[index];
- if (uniform) {
- result[1-index] = result[index];
- }
+ NR::Point const a = (snapped.getPoint() - origin); // vector to snapped point
+ NR::Point const b = (*i - origin); // vector to original point
+ result = NR::Point(NR_HUGE, NR_HUGE);
+ // If this point *i is horizontally or vertically aligned with
+ // the origin of the scaling, then it will scale purely in X or Y
+ // We can therefore only calculate the scaling in this direction
+ // and the scaling factor for the other direction should remain
+ // untouched (unless scaling is uniform ofcourse)
+ for (int index = 0; index < 2; index++) {
+ if (fabs(b[index]) > 1e-6) { // if SCALING CAN occur in this direction
+ if (fabs(fabs(a[index]/b[index]) - fabs(transformation[index])) > 1e-12) { // if SNAPPING DID occur in this direction
+ result[index] = a[index] / b[index]; // then calculate it!
}
+ // we might leave result[1-index] = NR_HUGE
+ // if scaling didn't occur in the other direction
}
}
- if (fabs(b[NR::X]) <= 1e-6 && fabs(b[NR::Y]) <= 1e-6) {
- metric = NR_HUGE;
- } else {
- // Compare the resulting scaling with the desired scaling
- metric = std::abs(NR::L2(result) - NR::L2(transformation));
- }
+ // Compare the resulting scaling with the desired scaling
+ scale_metric = result - transformation; // One or both of its components might be NR_HUGE
break;
}
case STRETCH:
- {
- for (int a = 0; a < 2; a++) {
- if (uniform || a == dim) {
- result[a] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
+ for (int index = 0; index < 2; index++) {
+ if (uniform || index == dim) {
+ result[index] = (snapped.getPoint()[dim] - origin[dim]) / ((*i)[dim] - origin[dim]);
} else {
- result[a] = 1;
+ result[index] = 1;
}
}
metric = std::abs(result[dim] - transformation[dim]);
break;
- }
case SKEW:
result[dim] = (snapped.getPoint()[dim] - (*i)[dim]) / ((*i)[1 - dim] - origin[1 - dim]);
metric = std::abs(result[dim] - transformation[dim]);
}
/* Note it if it's the best so far */
- bool const c1 = metric < best_metric;
- bool const c2 = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == false;
- bool const c3a = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == true;
- bool const c3b = second_metric < best_second_metric;
-
- if (c1 || c2 || c3a && c3b) {
- best_transformation = result;
- best_metric = metric;
- best_second_metric = second_metric;
- best_at_intersection = snapped.getAtIntersection();
- //std::cout << "SEL ";
- } //else { std::cout << " ";}
+ if (transformation_type == SCALE) {
+ for (int index = 0; index < 2; index++) {
+ if (fabs(scale_metric[index]) < fabs(best_scale_metric[index])) {
+ best_transformation[index] = result[index];
+ best_scale_metric[index] = fabs(scale_metric[index]);
+ //std::cout << "SEL ";
+ } //else { std::cout << " ";}
+ }
+ if (uniform) {
+ if (best_scale_metric[0] < best_scale_metric[1]) {
+ best_transformation[1] = best_transformation[0];
+ best_scale_metric[1] = best_scale_metric[0];
+ } else {
+ best_transformation[0] = best_transformation[1];
+ best_scale_metric[0] = best_scale_metric[1];
+ }
+ }
+ best_metric = std::min(best_scale_metric[0], best_scale_metric[1]);
+ //std::cout << "P_orig = " << (*i) << " | scale_metric = " << scale_metric << " | distance = " << snapped.getDistance() << " | P_snap = " << snapped.getPoint() << std::endl;
+ } else {
+ bool const c1 = metric < best_metric;
+ bool const c2 = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == false;
+ bool const c3a = metric == best_metric && snapped.getAtIntersection() == true && best_at_intersection == true;
+ bool const c3b = second_metric < best_second_metric;
+ bool const c4 = snapped.getAlwaysSnap() == true && best_always_snap == false;
+ bool const c4n = snapped.getAlwaysSnap() == false && best_always_snap == true;
+
+ if ((c1 || c2 || (c3a && c3b) || c4) && !c4n) {
+ best_transformation = result;
+ best_metric = metric;
+ best_second_metric = second_metric;
+ best_at_intersection = snapped.getAtIntersection();
+ best_always_snap = snapped.getAlwaysSnap();
+ //std::cout << "SEL ";
+ } //else { std::cout << " ";}
+ //std::cout << "P_orig = " << (*i) << " | metric = " << metric << " | distance = " << snapped.getDistance() << " | second metric = " << second_metric << " | P_snap = " << snapped.getPoint() << std::endl;
+ }
}
- //std::cout << "P_orig = " << (*i) << " | metric = " << metric << " | distance = " << snapped.getDistance() << " | second metric = " << second_metric << " | P_snap = " << snapped.getPoint() << std::endl;
+
j++;
}
+ if (transformation_type == SCALE) {
+ // When scaling, don't ever exit with one of scaling components set to NR_HUGE
+ if (best_transformation == NR::Point(NR_HUGE, NR_HUGE)) {
+ best_transformation == transformation; // return the original (i.e. un-snapped) transformation
+ } else {
+ // Still one of the transformation components could be NR_HUGE
+ for (int index = 0; index < 2; index++) {
+ if (best_transformation[index] == NR_HUGE) {
+ best_transformation[index] == uniform ? best_transformation[1-index] : transformation[index];
+ }
+ }
+ }
+ }
+
// Using " < 1e6" instead of " < NR_HUGE" for catching some rounding errors
// These rounding errors might be caused by NRRects, see bug #1584301
return std::make_pair(best_transformation, best_metric < 1e6);
@@ -669,54 +701,54 @@ std::pair<NR::Coord, bool> SnapManager::freeSnapSkew(Inkscape::Snapper::PointTyp
Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedConstraints &sc, bool constrained) const
{
- NR::Coord const guide_sens = guide.getDistance();
- NR::Coord grid_sens = 0;
+ NR::Coord const guide_tol = guide.getSnapperTolerance();
+ NR::Coord grid_tol = 0;
SnapManager::SnapperList const gs = getGridSnappers();
SnapperList::const_iterator i = gs.begin();
if (i != gs.end()) {
- grid_sens = (*i)->getDistance();
+ grid_tol = (*i)->getSnapperTolerance(); // there's only a single tolerance, equal for all grids
}
- // Store all snappoints, optionally together with their specific snapping range
- std::list<std::pair<Inkscape::SnappedPoint, NR::Coord> > sp_list;
+ // Store all snappoints
+ std::list<Inkscape::SnappedPoint> sp_list;
// Most of these snapped points are already within the snapping range, because
// they have already been filtered by their respective snappers. In that case
// we can set the snapping range to NR_HUGE here. If however we're looking at
// intersections of e.g. a grid and guide line, then we'll have to determine
// once again whether we're within snapping range. In this case we will set
- // the snapping range to e.g. min(guide_sens, grid_sens)
+ // the snapping range to e.g. min(guide_sens, grid_tol)
// search for the closest snapped point
Inkscape::SnappedPoint closestPoint;
if (getClosestSP(sc.points, closestPoint)) {
- sp_list.push_back(std::make_pair(closestPoint, NR_HUGE));
+ sp_list.push_back(closestPoint);
}
// search for the closest snapped line segment
Inkscape::SnappedLineSegment closestLineSegment;
if (getClosestSLS(sc.lines, closestLineSegment)) {
- sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestLineSegment), NR_HUGE));
+ sp_list.push_back(Inkscape::SnappedPoint(closestLineSegment));
}
if (_intersectionLS) {
// search for the closest snapped intersection of line segments
Inkscape::SnappedPoint closestLineSegmentIntersection;
if (getClosestIntersectionSLS(sc.lines, closestLineSegmentIntersection)) {
- sp_list.push_back(std::make_pair(closestLineSegmentIntersection, NR_HUGE));
+ sp_list.push_back(closestLineSegmentIntersection);
}
}
// search for the closest snapped grid line
Inkscape::SnappedLine closestGridLine;
if (getClosestSL(sc.grid_lines, closestGridLine)) {
- sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestGridLine), NR_HUGE));
+ sp_list.push_back(Inkscape::SnappedPoint(closestGridLine));
}
// search for the closest snapped guide line
Inkscape::SnappedLine closestGuideLine;
if (getClosestSL(sc.guide_lines, closestGuideLine)) {
- sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestGuideLine), NR_HUGE));
+ sp_list.push_back(Inkscape::SnappedPoint(closestGuideLine));
}
// When freely snapping to a grid/guide/path, only one degree of freedom is eliminated
@@ -729,44 +761,49 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedCons
// search for the closest snapped intersection of grid lines
Inkscape::SnappedPoint closestGridPoint;
if (getClosestIntersectionSL(sc.grid_lines, closestGridPoint)) {
- sp_list.push_back(std::make_pair(closestGridPoint, NR_HUGE));
+ sp_list.push_back(closestGridPoint);
}
// search for the closest snapped intersection of guide lines
Inkscape::SnappedPoint closestGuidePoint;
if (getClosestIntersectionSL(sc.guide_lines, closestGuidePoint)) {
- sp_list.push_back(std::make_pair(closestGuidePoint, NR_HUGE));
+ sp_list.push_back(closestGuidePoint);
}
// search for the closest snapped intersection of grid with guide lines
if (_intersectionGG) {
Inkscape::SnappedPoint closestGridGuidePoint;
if (getClosestIntersectionSL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) {
- sp_list.push_back(std::make_pair(closestGridGuidePoint, std::min(guide_sens, grid_sens)));
+ sp_list.push_back(closestGridGuidePoint);
}
}
}
// now let's see which snapped point gets a thumbs up
- Inkscape::SnappedPoint bestPoint(p, NR_HUGE);
- for (std::list<std::pair<Inkscape::SnappedPoint, NR::Coord> >::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) {
+ Inkscape::SnappedPoint bestSnappedPoint = Inkscape::SnappedPoint(p, NR_HUGE, 0, false);
+ 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
- if ((*i).first.getDistance() <= (*i).second) {
+ if ((*i).getDistance() <= (*i).getTolerance()) {
// if it's the first point
bool c1 = (i == sp_list.begin());
// or, if it's closer
- bool c2 = (*i).first.getDistance() < bestPoint.getDistance();
- // or, if it's just as close then consider the second distance
+ bool c2 = (*i).getDistance() < bestSnappedPoint.getDistance();
+ // or, if it's for a snapper with "always snap" turned on, and the previous wasn't
+ bool c3 = (*i).getAlwaysSnap() && !bestSnappedPoint.getAlwaysSnap();
+ // But in no case fall back from a snapper with "always snap" on to one with "always snap" off
+ bool c3n = !(*i).getAlwaysSnap() && bestSnappedPoint.getAlwaysSnap();
+ // or, if it's just as close then consider the second distance
// (which is only relevant for points at an intersection)
- bool c3a = ((*i).first.getDistance() == bestPoint.getDistance());
- bool c3b = (*i).first.getSecondDistance() < bestPoint.getSecondDistance();
+ bool c4a = ((*i).getDistance() == bestSnappedPoint.getDistance());
+ bool c4b = (*i).getSecondDistance() < bestSnappedPoint.getSecondDistance();
// then prefer this point over the previous one
- if (c1 || c2 || c3a && c3b) {
- bestPoint = (*i).first;
+ if ((c1 || c2 || c3 || (c4a && c4b)) && !c3n) {
+ bestSnappedPoint = *i;
}
}
}
- return bestPoint;
+
+ return bestSnappedPoint;
}
/*
diff --git a/src/snapped-line.cpp b/src/snapped-line.cpp
index 67fec627a85670a71a093d3e70d43880c76785f7..b6a5c2f9f5986f5b71692a808fad6a525cf26a80 100644 (file)
--- a/src/snapped-line.cpp
+++ b/src/snapped-line.cpp
#include <2geom/geom.h>
#include "libnr/nr-values.h"
-Inkscape::SnappedLineSegment::SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point start_point_of_line, NR::Point end_point_of_line)
+Inkscape::SnappedLineSegment::SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point start_point_of_line, NR::Point end_point_of_line)
: _start_point_of_line(start_point_of_line), _end_point_of_line(end_point_of_line)
{
- _distance = snapped_distance;
_point = snapped_point;
+ _distance = snapped_distance;
+ _tolerance = snapped_tolerance;
+ _always_snap = always_snap;
_at_intersection = false;
_second_distance = NR_HUGE;
+ _second_tolerance = 0;
+ _second_always_snap = false;
}
Inkscape::SnappedLineSegment::SnappedLineSegment()
{
_start_point_of_line = NR::Point(0,0);
_end_point_of_line = NR::Point(0,0);
- _distance = NR_HUGE;
_point = NR::Point(0,0);
+ _distance = NR_HUGE;
+ _tolerance = 0;
+ _always_snap = false;
_at_intersection = false;
_second_distance = NR_HUGE;
+ _second_tolerance = 0;
+ _second_always_snap = false;
}
Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegment const &line) const
{
Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
- NR::Coord distance = NR_HUGE;
- NR::Coord second_distance = NR_HUGE;
-
Geom::IntersectorKind result = segment_intersect(_start_point_of_line.to_2geom(), _end_point_of_line.to_2geom(),
line._start_point_of_line.to_2geom(), line._end_point_of_line.to_2geom(),
intersection_2geom);
NR::Point intersection(intersection_2geom);
if (result == Geom::intersects) {
- /* The relevant snapped distance is the distance to the closest snapped line, not the
- distance to the intersection. See the comment in Inkscape::SnappedLine::intersect
+ /* If a snapper has been told to "always snap", then this one should be preferred
+ * over the other, if that other one has not been told so. (The preferred snapper
+ * will be labelled "primary" below)
+ */
+ bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap(); //do not use _tolerance directly!
+ /* If neither or both have been told to "always snap", then cast a vote based on
+ * the snapped distance. For this we should consider the distance to the snapped
+ * line, not the distance to the intersection.
+ * See the comment in Inkscape::SnappedLine::intersect
*/
- distance = std::min(_distance, line.getDistance());
- second_distance = std::max(_distance, line.getDistance());
+ bool const c2 = _distance < line.getDistance();
+ bool const use_this_as_primary = c1 || c2;
+ Inkscape::SnappedLineSegment const *primarySLS = use_this_as_primary ? this : &line;
+ Inkscape::SnappedLineSegment const *secondarySLS = use_this_as_primary ? &line : this;
+ return SnappedPoint(intersection, primarySLS->getDistance(), primarySLS->getTolerance(), primarySLS->getAlwaysSnap(), true,
+ secondarySLS->getDistance(), secondarySLS->getTolerance(), secondarySLS->getAlwaysSnap());
}
- return SnappedPoint(intersection, distance, result == Geom::intersects, second_distance);
+
+ // No intersection
+ return SnappedPoint(intersection, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
};
-Inkscape::SnappedLine::SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point normal_to_line, NR::Point point_on_line)
+Inkscape::SnappedLine::SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point normal_to_line, NR::Point point_on_line)
: _normal_to_line(normal_to_line), _point_on_line(point_on_line)
{
_distance = snapped_distance;
+ _tolerance = snapped_tolerance;
+ _always_snap = always_snap;
_second_distance = NR_HUGE;
+ _second_tolerance = 0;
+ _second_always_snap = false;
_point = snapped_point;
_at_intersection = false;
}
_normal_to_line = NR::Point(0,0);
_point_on_line = NR::Point(0,0);
_distance = NR_HUGE;
+ _tolerance = 0;
+ _always_snap = false;
_second_distance = NR_HUGE;
+ _second_tolerance = 0;
+ _second_always_snap = false;
_point = NR::Point(0,0);
_at_intersection = false;
}
Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) const
{
- // Calculate the intersection of to lines, which are both within snapping range
+ // Calculate the intersection of two lines, which are both within snapping range
+ // One could be a grid line, whereas the other could be a guide line
// The point of intersection should be considered for snapping, but might be outside the snapping range
Geom::Point intersection_2geom(NR_HUGE, NR_HUGE);
- NR::Coord distance = NR_HUGE;
- NR::Coord second_distance = NR_HUGE;
-
- Geom::IntersectorKind result = Geom::line_intersection(getNormal().to_2geom(), getConstTerm(),
+ Geom::IntersectorKind result = Geom::line_intersection(getNormal().to_2geom(), getConstTerm(),
line.getNormal().to_2geom(), line.getConstTerm(), intersection_2geom);
NR::Point intersection(intersection_2geom);
if (result == Geom::intersects) {
- /* The relevant snapped distance is the distance to the closest snapped line, not the
- distance to the intersection. For example, when a box is almost aligned with a grid
- in both horizontal and vertical directions, the distance to the intersection of the
- grid lines will always be larger then the distance to a grid line. We will be snapping
- to the closest snapped point however, so if we ever want to snap to the intersection
- then the distance to it should at least be equal to the other distance, not greater
- than it, as that would rule the intersection out
- */
- distance = std::min(_distance, line.getDistance());
- second_distance = std::max(_distance, line.getDistance());
- }
-
- return SnappedPoint(intersection, distance, result == Geom::intersects, second_distance);
+ /* If a snapper has been told to "always snap", then this one should be preferred
+ * over the other, if that other one has not been told so. (The preferred snapper
+ * will be labelled "primary" below)
+ */
+ bool const c1 = this->getAlwaysSnap() && !line.getAlwaysSnap();
+ /* If neither or both have been told to "always snap", then cast a vote based on
+ * the snapped distance. For this we should consider the distance to the snapped
+ * line, not the distance to the intersection.
+ *
+ * The relevant snapped distance is the distance to the closest snapped line, not the
+ * distance to the intersection. For example, when a box is almost aligned with a grid
+ * in both horizontal and vertical directions, the distance to the intersection of the
+ * grid lines will always be larger then the distance to a grid line. We will be snapping
+ * to the closest snapped point however, so if we ever want to snap to the intersection
+ * then the distance to it should at least be equal to the other distance, not greater
+ * than it, as that would rule the intersection out when comparing it with regular snappoint,
+ * as the latter will always be closer
+ */
+ bool const c2 = _distance < line.getDistance();
+ bool const use_this_as_primary = c1 || c2;
+ Inkscape::SnappedLine const *primarySL = use_this_as_primary ? this : &line;
+ Inkscape::SnappedLine const *secondarySL = use_this_as_primary ? &line : this;
+ return SnappedPoint(intersection, primarySL->getDistance(), primarySL->getTolerance(), primarySL->getAlwaysSnap(), true,
+ secondarySL->getDistance(), secondarySL->getTolerance(), secondarySL->getAlwaysSnap());
+ }
+
+ // No intersection
+ return SnappedPoint(intersection, NR_HUGE, 0, false, false, NR_HUGE, 0, false);
}
// search for the closest snapped line segment
diff --git a/src/snapped-line.h b/src/snapped-line.h
index be2a792b72302407cab27e2069ed6667e5785707..3616058b0cb0d6e8a9195d68230fb501c998e953 100644 (file)
--- a/src/snapped-line.h
+++ b/src/snapped-line.h
{
public:
SnappedLineSegment();
- SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point start_point_of_line, NR::Point end_point_of_line);
+ SnappedLineSegment(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point start_point_of_line, NR::Point end_point_of_line);
~SnappedLineSegment();
Inkscape::SnappedPoint intersect(SnappedLineSegment const &line) const; //intersect with another SnappedLineSegment
{
public:
SnappedLine();
- SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Point normal_to_line, NR::Point point_on_line);
+ SnappedLine(NR::Point snapped_point, NR::Coord snapped_distance, NR::Coord snapped_tolerance, bool always_snap, NR::Point normal_to_line, NR::Point point_on_line);
~SnappedLine();
Inkscape::SnappedPoint intersect(SnappedLine const &line) const; //intersect with another SnappedLine
// This line is described by this equation:
// a*x + b*y = c <-> nx*px + ny+py = c <-> n.p = c
NR::Point getNormal() const {return _normal_to_line;} // n = (nx, ny)
- NR::Point getPointOnLine() const {return _point_on_line;} // p = (px, py)
+ NR::Point getPointOnLine() const {return _point_on_line;} // p = (px, py)
NR::Coord getConstTerm() const {return dot(_normal_to_line, _point_on_line);} // c = n.p = nx*px + ny*py;
private:
diff --git a/src/snapped-point.cpp b/src/snapped-point.cpp
index d97abda1d0a7567b952a2e49c9aa741a2ac44a69..8a0aea3c7594687e876fe7bdd14259313e7c9d3c 100644 (file)
--- a/src/snapped-point.cpp
+++ b/src/snapped-point.cpp
#include "snapped-point.h"
-Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, bool at_intersection, NR::Coord d2)
- : _distance(d), _point(p), _at_intersection(at_intersection), _second_distance(d2)
+// overloaded constructor
+Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a)
+ : _point(p), _distance(d), _tolerance(t), _always_snap(a)
+{
+ _at_intersection = false;
+ _second_distance = NR_HUGE;
+ _second_tolerance = 0;
+ _second_always_snap = false;
+}
+
+Inkscape::SnappedPoint::SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a, bool at_intersection, NR::Coord d2, NR::Coord t2, bool a2)
+ : _point(p), _distance(d), _tolerance(t), _always_snap(a), _at_intersection(at_intersection),
+ _second_distance(d2), _second_tolerance(t2), _second_always_snap(a2)
{
}
Inkscape::SnappedPoint::SnappedPoint()
{
- _distance = NR_HUGE;
_point = NR::Point(0,0);
+ _distance = NR_HUGE;
+ _tolerance = 0;
+ _always_snap = false;
_at_intersection = false;
_second_distance = NR_HUGE;
+ _second_tolerance = 0;
+ _second_always_snap = false;
}
return _distance;
}
+NR::Coord Inkscape::SnappedPoint::getTolerance() const
+{
+ return _tolerance;
+}
+
+bool Inkscape::SnappedPoint::getAlwaysSnap() const
+{
+ return _always_snap;
+}
+
NR::Coord Inkscape::SnappedPoint::getSecondDistance() const
{
return _second_distance;
}
+NR::Coord Inkscape::SnappedPoint::getSecondTolerance() const
+{
+ return _second_tolerance;
+}
+
+bool Inkscape::SnappedPoint::getSecondAlwaysSnap() const
+{
+ return _second_always_snap;
+}
+
NR::Point Inkscape::SnappedPoint::getPoint() const
{
diff --git a/src/snapped-point.h b/src/snapped-point.h
index e4b08a302624d2122bfc38bc854b41323d44446e..f0584812d8a9814b6106067b01e87b06c9528880 100644 (file)
--- a/src/snapped-point.h
+++ b/src/snapped-point.h
{
public:
SnappedPoint();
- SnappedPoint(::NR::Point p, ::NR::Coord d, bool at_intersection = false, NR::Coord d2 = NR_HUGE);
+ SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a, bool at_intersection, NR::Coord d2, NR::Coord t2, bool a2);
+ SnappedPoint(NR::Point p, NR::Coord d, NR::Coord t, bool a);
~SnappedPoint();
NR::Coord getDistance() const;
+ NR::Coord getTolerance() const;
+ bool getAlwaysSnap() const;
NR::Coord getSecondDistance() const;
+ NR::Coord getSecondTolerance() const;
+ bool getSecondAlwaysSnap() const;
NR::Point getPoint() const;
bool getAtIntersection() const {return _at_intersection;}
an intersection of e.g. two lines, then this is the distance to the closest
line */
NR::Coord _distance;
+ /* The snapping tolerance in screen pixels (depends on zoom)*/
+ NR::Coord _tolerance;
+ /* If true then "Always snap" is on */
+ bool _always_snap;
/* If the snapped point is at an intersection of e.g. two lines, then this is
the distance to the fartest line */
- NR::Coord _second_distance;
-
-
+ NR::Coord _second_distance;
+ /* The snapping tolerance in screen pixels (depends on zoom)*/
+ NR::Coord _second_tolerance;
+ /* If true then "Always snap" is on */
+ bool _second_always_snap;
};
}
diff --git a/src/snapper.cpp b/src/snapper.cpp
index 1daa1d15183574d90839e1f668ec014b11155d3c..efdc6bcbea8a4bebcd8630f56108a284187cb2bf 100644 (file)
--- a/src/snapper.cpp
+++ b/src/snapper.cpp
/**
* Construct new Snapper for named view.
* \param nv Named view.
- * \param d Snap distance.
+ * \param d Snap tolerance.
*/
-Inkscape::Snapper::Snapper(SPNamedView const *nv, NR::Coord const d) : _named_view(nv), _snap_enabled(true), _distance(d)
+Inkscape::Snapper::Snapper(SPNamedView const *nv, NR::Coord const t) : _named_view(nv), _snap_enabled(true), _snapper_tolerance(t)
{
g_assert(_named_view != NULL);
g_assert(SP_IS_NAMEDVIEW(_named_view));
@@ -31,20 +31,25 @@ Inkscape::Snapper::Snapper(SPNamedView const *nv, NR::Coord const d) : _named_vi
}
/**
- * Set snap distance.
- * \param d New snap distance (desktop coordinates)
+ * Set snap tolerance.
+ * \param d New snap tolerance (desktop coordinates)
*/
-void Inkscape::Snapper::setDistance(NR::Coord const d)
+void Inkscape::Snapper::setSnapperTolerance(NR::Coord const d)
{
- _distance = d;
+ _snapper_tolerance = d;
}
/**
- * \return Snap distance (desktop coordinates); depends on current zoom so that it's always the same in screen pixels
+ * \return Snap tolerance (desktop coordinates); depends on current zoom so that it's always the same in screen pixels
*/
-NR::Coord Inkscape::Snapper::getDistance() const
+NR::Coord Inkscape::Snapper::getSnapperTolerance() const
{
- return _distance / SP_ACTIVE_DESKTOP->current_zoom();
+ return _snapper_tolerance / SP_ACTIVE_DESKTOP->current_zoom();
+}
+
+bool Inkscape::Snapper::getSnapperAlwaysSnap() const
+{
+ return _snapper_tolerance == 10000; //TODO: Replace this threshold of 10000 by a constant; see also tolerance-slider.cpp
}
/**
diff --git a/src/snapper.h b/src/snapper.h
index cf2533f22fb3ebb1898c1ac33517b2ac99305ba9..8aeea0a26c60bf15eacc170604df0ecf46f1c6c1 100644 (file)
--- a/src/snapper.h
+++ b/src/snapper.h
*
* Authors:
* Carl Hetherington <inkscape@carlh.net>
+ * Diederik van Lierop <mail@diedenrezi.nl>
*
* Released under GNU GPL, read the file 'COPYING' for more information.
*/
static const PointType SNAPPOINT_GUIDE;
void setSnapFrom(PointType t, bool s);
- void setDistance(::NR::Coord d);
-
bool getSnapFrom(PointType t) const;
- ::NR::Coord getDistance() const;
-
+
+ void setSnapperTolerance(NR::Coord t);
+ NR::Coord getSnapperTolerance() const; //returns the tolerance of the snapper in screen pixels (i.e. independent of zoom)
+ bool getSnapperAlwaysSnap() const; //if true, then the snapper will always snap, regardless of its tolerance
+
/**
* \return true if this Snapper will snap at least one kind of point.
*/
bool _snap_enabled; ///< true if this snapper is enabled, otherwise false
private:
+ NR::Coord _snapper_tolerance; ///< snap tolerance in desktop coordinates
+ // must be private to enforce the usage of getTolerance(), which retrieves
+ // the tolerance in screen pixels (making it zoom independent)
+
/**
* Try to snap a point to whatever this snapper is interested in. Any
std::vector<NR::Point> &points_to_snap,
ConstraintLine const &c,
std::list<SPItem const *> const &it) const = 0;
-
- NR::Coord _distance; ///< snap distance (desktop coordinates)
};
}