From 05c7fca68caa795eaec938800fe3679e831d8342 Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Sun, 2 Aug 2009 12:32:58 +0000 Subject: [PATCH] Snap to a guide's origin too (resulting in a 2D constraint), instead of only to the guide itself (only 1D constraint) --- src/display/snap-indicator.cpp | 317 +++++++++++++++++---------------- src/guide-snapper.cpp | 27 +-- src/guide-snapper.h | 1 + src/line-snapper.cpp | 52 ++++-- src/line-snapper.h | 6 +- src/snapped-point.h | 1 + 6 files changed, 225 insertions(+), 179 deletions(-) diff --git a/src/display/snap-indicator.cpp b/src/display/snap-indicator.cpp index 081496fd0..20ea7d58c 100644 --- a/src/display/snap-indicator.cpp +++ b/src/display/snap-indicator.cpp @@ -26,8 +26,8 @@ namespace Display { SnapIndicator::SnapIndicator(SPDesktop * desktop) : _snaptarget(NULL), - _snaptarget_tooltip(NULL), - _snapsource(NULL), + _snaptarget_tooltip(NULL), + _snapsource(NULL), _desktop(desktop) { } @@ -35,14 +35,14 @@ SnapIndicator::SnapIndicator(SPDesktop * desktop) SnapIndicator::~SnapIndicator() { // remove item that might be present - remove_snaptarget(); - remove_snapsource(); + remove_snaptarget(); + remove_snapsource(); } void SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) { - remove_snaptarget(); //only display one snaptarget at a time + remove_snaptarget(); //only display one snaptarget at a time g_assert(_desktop != NULL); @@ -59,191 +59,194 @@ SnapIndicator::set_new_snaptarget(Inkscape::SnappedPoint const p) // TRANSLATORS: undefined target for snapping gchar *target_name = _("UNDEFINED"); switch (p.getTarget()) { - case SNAPTARGET_UNDEFINED: - target_name = _("UNDEFINED"); - break; - case SNAPTARGET_GRID: - target_name = _("grid line"); - break; + case SNAPTARGET_UNDEFINED: + target_name = _("UNDEFINED"); + break; + case SNAPTARGET_GRID: + target_name = _("grid line"); + break; case SNAPTARGET_GRID_INTERSECTION: - target_name = _("grid intersection"); - break; + target_name = _("grid intersection"); + break; case SNAPTARGET_GUIDE: - target_name = _("guide"); - break; + target_name = _("guide"); + break; case SNAPTARGET_GUIDE_INTERSECTION: - target_name = _("guide intersection"); - break; + target_name = _("guide intersection"); + break; + case SNAPTARGET_GUIDE_ORIGIN: + target_name = _("guide origin"); + break; case SNAPTARGET_GRID_GUIDE_INTERSECTION: - target_name = _("grid-guide intersection"); - break; + target_name = _("grid-guide intersection"); + break; case SNAPTARGET_NODE_CUSP: - target_name = _("cusp node"); - break; + target_name = _("cusp node"); + break; case SNAPTARGET_NODE_SMOOTH: - target_name = _("smooth node"); - break; - case SNAPTARGET_PATH: - target_name = _("path"); - break; + target_name = _("smooth node"); + break; + case SNAPTARGET_PATH: + target_name = _("path"); + break; case SNAPTARGET_PATH_INTERSECTION: - target_name = _("path intersection"); - break; + target_name = _("path intersection"); + break; case SNAPTARGET_BBOX_CORNER: - target_name = _("bounding box corner"); - break; + target_name = _("bounding box corner"); + break; case SNAPTARGET_BBOX_EDGE: - target_name = _("bounding box side"); - break; + target_name = _("bounding box side"); + break; case SNAPTARGET_GRADIENTS_PARENT_BBOX: - target_name = _("bounding box"); - break; + target_name = _("bounding box"); + break; case SNAPTARGET_PAGE_BORDER: - target_name = _("page border"); - break; + target_name = _("page border"); + break; case SNAPTARGET_LINE_MIDPOINT: - target_name = _("line midpoint"); - break; + target_name = _("line midpoint"); + break; case SNAPTARGET_OBJECT_MIDPOINT: - target_name = _("object midpoint"); - break; + target_name = _("object midpoint"); + break; case SNAPTARGET_ROTATION_CENTER: - target_name = _("object rotation center"); - break; + target_name = _("object rotation center"); + break; case SNAPTARGET_HANDLE: - target_name = _("handle"); - break; + target_name = _("handle"); + break; case SNAPTARGET_BBOX_EDGE_MIDPOINT: - target_name = _("bounding box side midpoint"); - break; + target_name = _("bounding box side midpoint"); + break; case SNAPTARGET_BBOX_MIDPOINT: - target_name = _("bounding box midpoint"); - break; + target_name = _("bounding box midpoint"); + break; case SNAPTARGET_PAGE_CORNER: - target_name = _("page corner"); - break; + target_name = _("page corner"); + break; case SNAPTARGET_CONVEX_HULL_CORNER: - target_name = _("convex hull corner"); - break; + target_name = _("convex hull corner"); + break; case SNAPTARGET_ELLIPSE_QUADRANT_POINT: - target_name = _("quadrant point"); - break; + target_name = _("quadrant point"); + break; case SNAPTARGET_CENTER: - target_name = _("center"); - break; + target_name = _("center"); + break; case SNAPTARGET_CORNER: - target_name = _("corner"); - break; + target_name = _("corner"); + break; case SNAPTARGET_TEXT_BASELINE: - target_name = _("text baseline"); - break; + target_name = _("text baseline"); + break; default: - g_warning("Snap target has not yet been defined!"); + g_warning("Snap target has not yet been defined!"); break; } gchar *source_name = _("UNDEFINED"); - switch (p.getSource()) { - case SNAPSOURCE_UNDEFINED: - source_name = _("UNDEFINED"); - break; - case SNAPSOURCE_BBOX_CORNER: - source_name = _("Bounding box corner"); - break; - case SNAPSOURCE_BBOX_MIDPOINT: - source_name = _("Bounding box midpoint"); - break; - case SNAPSOURCE_BBOX_EDGE_MIDPOINT: - source_name = _("Bounding box side midpoint"); - break; - case SNAPSOURCE_NODE_SMOOTH: - source_name = _("Smooth node"); - break; - case SNAPSOURCE_NODE_CUSP: - source_name = _("Cusp node"); - break; - case SNAPSOURCE_LINE_MIDPOINT: - source_name = _("Line midpoint"); - break; - case SNAPSOURCE_OBJECT_MIDPOINT: - source_name = _("Object midpoint"); - break; - case SNAPSOURCE_ROTATION_CENTER: - source_name = _("Object rotation center"); - break; - case SNAPSOURCE_HANDLE: - source_name = _("Handle"); - break; - case SNAPSOURCE_PATH_INTERSECTION: - source_name = _("Path intersection"); - break; - case SNAPSOURCE_GUIDE: - source_name = _("Guide"); - break; - case SNAPSOURCE_GUIDE_ORIGIN: - source_name = _("Guide origin"); - break; - case SNAPSOURCE_CONVEX_HULL_CORNER: - source_name = _("Convex hull corner"); - break; - case SNAPSOURCE_ELLIPSE_QUADRANT_POINT: - source_name = _("Quadrant point"); - break; - case SNAPSOURCE_CENTER: - source_name = _("Center"); - break; - case SNAPSOURCE_CORNER: - source_name = _("Corner"); - break; - case SNAPSOURCE_TEXT_BASELINE: - source_name = _("Text baseline"); - break; - default: - g_warning("Snap source has not yet been defined!"); - break; - } + switch (p.getSource()) { + case SNAPSOURCE_UNDEFINED: + source_name = _("UNDEFINED"); + break; + case SNAPSOURCE_BBOX_CORNER: + source_name = _("Bounding box corner"); + break; + case SNAPSOURCE_BBOX_MIDPOINT: + source_name = _("Bounding box midpoint"); + break; + case SNAPSOURCE_BBOX_EDGE_MIDPOINT: + source_name = _("Bounding box side midpoint"); + break; + case SNAPSOURCE_NODE_SMOOTH: + source_name = _("Smooth node"); + break; + case SNAPSOURCE_NODE_CUSP: + source_name = _("Cusp node"); + break; + case SNAPSOURCE_LINE_MIDPOINT: + source_name = _("Line midpoint"); + break; + case SNAPSOURCE_OBJECT_MIDPOINT: + source_name = _("Object midpoint"); + break; + case SNAPSOURCE_ROTATION_CENTER: + source_name = _("Object rotation center"); + break; + case SNAPSOURCE_HANDLE: + source_name = _("Handle"); + break; + case SNAPSOURCE_PATH_INTERSECTION: + source_name = _("Path intersection"); + break; + case SNAPSOURCE_GUIDE: + source_name = _("Guide"); + break; + case SNAPSOURCE_GUIDE_ORIGIN: + source_name = _("Guide origin"); + break; + case SNAPSOURCE_CONVEX_HULL_CORNER: + source_name = _("Convex hull corner"); + break; + case SNAPSOURCE_ELLIPSE_QUADRANT_POINT: + source_name = _("Quadrant point"); + break; + case SNAPSOURCE_CENTER: + source_name = _("Center"); + break; + case SNAPSOURCE_CORNER: + source_name = _("Corner"); + break; + case SNAPSOURCE_TEXT_BASELINE: + source_name = _("Text baseline"); + break; + default: + g_warning("Snap source has not yet been defined!"); + break; + } //std::cout << "Snapped " << source_name << " to " << target_name << std::endl; - remove_snapsource(); // Don't set both the source and target indicators, as these will overlap + remove_snapsource(); // Don't set both the source and target indicators, as these will overlap // Display the snap indicator (i.e. the cross) SPCanvasItem * canvasitem = NULL; - if (p.getTarget() == SNAPTARGET_NODE_SMOOTH || p.getTarget() == SNAPTARGET_NODE_CUSP) { - canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), - SP_TYPE_CTRL, - "anchor", GTK_ANCHOR_CENTER, - "size", 10.0, - "stroked", TRUE, - "stroke_color", 0xf000f0ff, - "mode", SP_KNOT_MODE_XOR, - "shape", SP_KNOT_SHAPE_DIAMOND, - NULL ); - } else { - canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), - SP_TYPE_CTRL, - "anchor", GTK_ANCHOR_CENTER, - "size", 10.0, - "stroked", TRUE, - "stroke_color", 0xf000f0ff, - "mode", SP_KNOT_MODE_XOR, - "shape", SP_KNOT_SHAPE_CROSS, - NULL ); - } + if (p.getTarget() == SNAPTARGET_NODE_SMOOTH || p.getTarget() == SNAPTARGET_NODE_CUSP) { + canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), + SP_TYPE_CTRL, + "anchor", GTK_ANCHOR_CENTER, + "size", 10.0, + "stroked", TRUE, + "stroke_color", 0xf000f0ff, + "mode", SP_KNOT_MODE_XOR, + "shape", SP_KNOT_SHAPE_DIAMOND, + NULL ); + } else { + canvasitem = sp_canvas_item_new(sp_desktop_tempgroup (_desktop), + SP_TYPE_CTRL, + "anchor", GTK_ANCHOR_CENTER, + "size", 10.0, + "stroked", TRUE, + "stroke_color", 0xf000f0ff, + "mode", SP_KNOT_MODE_XOR, + "shape", SP_KNOT_SHAPE_CROSS, + NULL ); + } - const int timeout_val = 1200; // TODO add preference for snap indicator timeout? + const int timeout_val = 1200; // TODO add preference for snap indicator timeout? - SP_CTRL(canvasitem)->moveto(p.getPoint()); - _snaptarget = _desktop->add_temporary_canvasitem(canvasitem, timeout_val); + SP_CTRL(canvasitem)->moveto(p.getPoint()); + _snaptarget = _desktop->add_temporary_canvasitem(canvasitem, timeout_val); - gchar *tooltip_str = g_strconcat(source_name, _(" to "), target_name, NULL); - Geom::Point tooltip_pos = p.getPoint() + _desktop->w2d(Geom::Point(15, -15)); + gchar *tooltip_str = g_strconcat(source_name, _(" to "), target_name, NULL); + Geom::Point tooltip_pos = p.getPoint() + _desktop->w2d(Geom::Point(15, -15)); - SPCanvasItem *canvas_tooltip = sp_canvastext_new(sp_desktop_tempgroup(_desktop), _desktop, tooltip_pos, tooltip_str); - g_free(tooltip_str); + SPCanvasItem *canvas_tooltip = sp_canvastext_new(sp_desktop_tempgroup(_desktop), _desktop, tooltip_pos, tooltip_str); + g_free(tooltip_str); - sp_canvastext_set_anchor((SPCanvasText* )canvas_tooltip, -1, 1); - _snaptarget_tooltip = _desktop->add_temporary_canvasitem(canvas_tooltip, timeout_val); - } + sp_canvastext_set_anchor((SPCanvasText* )canvas_tooltip, -1, 1); + _snaptarget_tooltip = _desktop->add_temporary_canvasitem(canvas_tooltip, timeout_val); + } } void @@ -255,16 +258,16 @@ SnapIndicator::remove_snaptarget() } if (_snaptarget_tooltip) { - _desktop->remove_temporary_canvasitem(_snaptarget_tooltip); - _snaptarget_tooltip = NULL; - } + _desktop->remove_temporary_canvasitem(_snaptarget_tooltip); + _snaptarget_tooltip = NULL; + } } void SnapIndicator::set_new_snapsource(std::pair const p) { - remove_snapsource(); + remove_snapsource(); g_assert(_desktop != NULL); @@ -284,7 +287,7 @@ SnapIndicator::set_new_snapsource(std::pair const p) SP_CTRL(canvasitem)->moveto(p.first); _snapsource = _desktop->add_temporary_canvasitem(canvasitem, 1000); - } + } } void diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index 3a9a861e5..5cf97958a 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -26,9 +26,9 @@ Inkscape::GuideSnapper::GuideSnapper(SnapManager *sm, Geom::Coord const d) : Lin */ Geom::Coord Inkscape::GuideSnapper::getSnapperTolerance() const { - SPDesktop const *dt = _snapmanager->getDesktop(); - double const zoom = dt ? dt->current_zoom() : 1; - return _snapmanager->snapprefs.getGuideTolerance() / zoom; + SPDesktop const *dt = _snapmanager->getDesktop(); + double const zoom = dt ? dt->current_zoom() : 1; + return _snapmanager->snapprefs.getGuideTolerance() / zoom; } bool Inkscape::GuideSnapper::getSnapperAlwaysSnap() const @@ -49,7 +49,7 @@ Inkscape::GuideSnapper::LineList Inkscape::GuideSnapper::_getSnapLines(Geom::Poi for (GSList const *l = _snapmanager->getNamedView()->guides; l != NULL; l = l->next) { SPGuide const *g = SP_GUIDE(l->data); if (g != guide_to_ignore) { - s.push_back(std::make_pair(g->normal_to_line, g->point_on_line)); + s.push_back(std::make_pair(g->normal_to_line, g->point_on_line)); } } @@ -61,11 +61,11 @@ Inkscape::GuideSnapper::LineList Inkscape::GuideSnapper::_getSnapLines(Geom::Poi */ bool Inkscape::GuideSnapper::ThisSnapperMightSnap() const { - if (_snapmanager->getNamedView() == NULL) { - return false; - } + if (_snapmanager->getNamedView() == NULL) { + return false; + } - return (_snap_enabled && _snapmanager->snapprefs.getSnapToGuides() && _snapmanager->getNamedView()->showguides); + return (_snap_enabled && _snapmanager->snapprefs.getSnapToGuides() && _snapmanager->getNamedView()->showguides); } void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const @@ -74,10 +74,17 @@ void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point sc.guide_lines.push_back(dummy); } +void Inkscape::GuideSnapper::_addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source) const +{ + SnappedPoint dummy = SnappedPoint(origin, source, Inkscape::SNAPTARGET_GUIDE_ORIGIN, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + sc.points.push_back(dummy); +} + + void Inkscape::GuideSnapper::_addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const { - SnappedPoint dummy = SnappedPoint(snapped_point, source, Inkscape::SNAPTARGET_GUIDE, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); - sc.points.push_back(dummy); + SnappedPoint dummy = SnappedPoint(snapped_point, source, Inkscape::SNAPTARGET_GUIDE, snapped_distance, getSnapperTolerance(), getSnapperAlwaysSnap(), true); + sc.points.push_back(dummy); } /* diff --git a/src/guide-snapper.h b/src/guide-snapper.h index f9f433bf4..1dc602f72 100644 --- a/src/guide-snapper.h +++ b/src/guide-snapper.h @@ -35,6 +35,7 @@ public: private: LineList _getSnapLines(Geom::Point const &p) const; void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const; + void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source) const; void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const; }; diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index 73f46c0a2..5d5a77280 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -14,7 +14,7 @@ #include <2geom/line.h> #include "line-snapper.h" #include "snapped-line.h" -#include +//#include #include "snap.h" Inkscape::LineSnapper::LineSnapper(SnapManager *sm, Geom::Coord const d) : Snapper(sm, d) @@ -30,7 +30,7 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, std::vector const */*it*/, std::vector > */*unselected_nodes*/) const { - if (!(_snap_enabled && _snapmanager->snapprefs.getSnapFrom(t)) ) { + if (!(_snap_enabled && _snapmanager->snapprefs.getSnapFrom(t)) ) { return; } @@ -50,6 +50,15 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, //Store any line that's within snapping range if (dist < getSnapperTolerance()) { _addSnappedLine(sc, p_proj, dist, source_type, i->first, i->second); + // For any line that's within range, we will also look at it's "point on line" p1. For guides + // this point coincides with its origin; for grids this is of no use, but we cannot + // discern between grids and guides here + Geom::Coord const dist_p1 = Geom::L2(p1 - p); + if (dist_p1 < getSnapperTolerance()) { + _addSnappedLinesOrigin(sc, p1, dist_p1, source_type); + // Only relevant for guides; grids don't have an origin per line + // Therefore _addSnappedLinesOrigin() will only be implemented for guides + } // std::cout << " -> distance = " << dist; } // std::cout << std::endl; @@ -75,35 +84,56 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) { if (Geom::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint - Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p; + // constraint line + Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p; Geom::Line line1(point_on_line, point_on_line + c.getDirection()); - Geom::Line line2(i->second, i->second + Geom::rot90(i->first)); + + // grid/guide line + Geom::Point const p1 = i->second; // point at guide/grid line + Geom::Point const p2 = p1 + Geom::rot90(i->first); // 2nd point at guide/grid line + Geom::Line line2(p1, p2); + Geom::OptCrossing inters = Geom::OptCrossing(); // empty by default try { - inters = Geom::intersection(line1, line2); + inters = Geom::intersection(line1, line2); } catch (Geom::InfiniteSolutions e) { - // We're probably dealing with parallel lines, so snapping doesn't make any sense here - continue; // jump to the next iterator in the for-loop + // We're probably dealing with parallel lines, so snapping doesn't make any sense here + continue; // jump to the next iterator in the for-loop } - if (inters) { - Geom::Point t = line1.pointAt((*inters).ta); - const Geom::Coord dist = Geom::L2(t - p); + if (inters) { + Geom::Point t = line1.pointAt((*inters).ta); + const Geom::Coord dist = Geom::L2(t - p); if (dist < getSnapperTolerance()) { - // When doing a constrained snap, we're already at an intersection. + // 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 _addSnappedPoint(sc, t, dist, source_type); + // For any line that's within range, we will also look at it's "point on line" p1. For guides + // this point coincides with its origin; for grids this is of no use, but we cannot + // discern between grids and guides here + Geom::Coord const dist_p1 = Geom::L2(p1 - p); + if (dist_p1 < getSnapperTolerance()) { + _addSnappedLinesOrigin(sc, p1, dist_p1, source_type); + // Only relevant for guides; grids don't have an origin per line + // Therefore _addSnappedLinesOrigin() will only be implemented for guides + } } } } } } +// Will only be overridden in the guide-snapper class, because grid lines don't have an origin; the +// grid-snapper classes will use this default empty method +void Inkscape::LineSnapper::_addSnappedLinesOrigin(SnappedConstraints &/*sc*/, Geom::Point const /*origin*/, Geom::Coord const /*snapped_distance*/, SnapSourceType const &/*source_type*/) const +{ +} + /* Local Variables: mode:c++ diff --git a/src/line-snapper.h b/src/line-snapper.h index 4c971d238..4ad08a99f 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -7,7 +7,7 @@ * * Authors: * Carl Hetherington - * Diederik van Lierop + * Diederik van Lierop * * Copyright (C) 1999-2008 Authors * @@ -55,6 +55,10 @@ private: virtual LineList _getSnapLines(Geom::Point const &p) const = 0; virtual void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source, Geom::Point const normal_to_line, Geom::Point const point_on_line) const = 0; + + // Will only be implemented for guide lines, because grid lines don't have an origin + virtual void _addSnappedLinesOrigin(SnappedConstraints &sc, Geom::Point const origin, Geom::Coord const snapped_distance, SnapSourceType const &source) const; + virtual void _addSnappedPoint(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, SnapSourceType const &source) const = 0; }; diff --git a/src/snapped-point.h b/src/snapped-point.h index d071afddc..70d16b0be 100644 --- a/src/snapped-point.h +++ b/src/snapped-point.h @@ -26,6 +26,7 @@ enum SnapTargetType { SNAPTARGET_GRID_INTERSECTION, SNAPTARGET_GUIDE, SNAPTARGET_GUIDE_INTERSECTION, + SNAPTARGET_GUIDE_ORIGIN, SNAPTARGET_GRID_GUIDE_INTERSECTION, SNAPTARGET_NODE_SMOOTH, SNAPTARGET_NODE_CUSP, -- 2.30.2