From: dvlierop2 Date: Sun, 18 Jan 2009 20:00:19 +0000 (+0000) Subject: - Snap to the midpoint of shapes and bboxes X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=332f5d0034d0091fd568fb1fa598cc2092121dd2;p=inkscape.git - Snap to the midpoint of shapes and bboxes - Snap to the midpoint of paths and bbox edges - Refactor the snapping toggle, i.e. what snaps to what when toggling a specific option in the snapping toolbar PS: icons.svg has been modified, so use "make install" if needed --- diff --git a/share/icons/icons.svg b/share/icons/icons.svg index 297e9aa1d..10b3a6d05 100644 --- a/share/icons/icons.svg +++ b/share/icons/icons.svg @@ -1,5 +1,5 @@ - + @@ -703,8 +703,15 @@ + + + + + + + - + @@ -727,7 +734,7 @@ http://www.inkscape.org/ Inkscape Developers - + @@ -3228,32 +3235,32 @@ http://www.inkscape.org/ - - - + + + - - + + - - - + + + - - + + - + - + @@ -3263,31 +3270,48 @@ http://www.inkscape.org/ - + - - + + - + - - - + + + - - + + - - + + + + + + + + + + + + + + + + + + + diff --git a/src/attributes-test.h b/src/attributes-test.h index 5e3f741d7..1148953ec 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -352,7 +352,10 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:snap-center", true}, {"inkscape:snap-smooth-nodes", true}, {"inkscape:snap-midpoints", true}, - {"inkscape:snap-intersection-grid-guide", true}, + {"inkscape:snap-object-midpoints", true}, + {"inkscape:snap-bbox-edge-midpoints", true}, + {"inkscape:snap-bbox-midpoints", true}, + {"inkscape:snap-intersection-grid-guide", true}, {"inkscape:snap-intersection-paths", true}, {"inkscape:pageopacity", true}, {"inkscape:pageshadow", true}, diff --git a/src/attributes.cpp b/src/attributes.cpp index 1ee33ef83..049632e50 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -92,8 +92,11 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_SNAP_GUIDE, "inkscape:snap-guide"}, {SP_ATTR_INKSCAPE_SNAP_CENTER, "inkscape:snap-center"}, {SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES, "inkscape:snap-smooth-nodes"}, - {SP_ATTR_INKSCAPE_SNAP_MIDPOINTS, "inkscape:snap-midpoints"}, - {SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, "inkscape:snap-intersection-grid-guide"}, + {SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS, "inkscape:snap-midpoints"}, + {SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS, "inkscape:snap-object-midpoints"}, + {SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS, "inkscape:snap-bbox-edge-midpoints"}, + {SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, "inkscape:snap-bbox-midpoints"}, + {SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, "inkscape:snap-intersection-grid-guide"}, {SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, "inkscape:snap-intersection-paths"}, {SP_ATTR_INKSCAPE_OBJECT_PATHS, "inkscape:object-paths"}, {SP_ATTR_INKSCAPE_OBJECT_NODES, "inkscape:object-nodes"}, diff --git a/src/attributes.h b/src/attributes.h index 68e6135db..6fe3ebb80 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -92,7 +92,10 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_SNAP_GUIDE, SP_ATTR_INKSCAPE_SNAP_CENTER, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES, - SP_ATTR_INKSCAPE_SNAP_MIDPOINTS, + SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS, + SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS, + SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS, + SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS, SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, SP_ATTR_INKSCAPE_OBJECT_PATHS, diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index be2e84170..b05466ede 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -43,7 +43,11 @@ Inkscape::GuideSnapper::LineList Inkscape::GuideSnapper::_getSnapLines(Geom::Poi */ bool Inkscape::GuideSnapper::ThisSnapperMightSnap() const { - return _snapmanager->getNamedView() == NULL ? false : (_snap_enabled && _snapmanager->snapprefs.getSnapModeBBoxOrNodes() && _snapmanager->getNamedView()->showguides); + if (_snapmanager->getNamedView() == NULL) { + return false; + } + + return (_snap_enabled && _snapmanager->snapprefs.getSnapModeBBoxOrNodes() && _snapmanager->getNamedView()->showguides); } void Inkscape::GuideSnapper::_addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, Geom::Point const normal_to_line, Geom::Point const point_on_line) const diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index bfcda3f79..bc43d640b 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -31,7 +31,7 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, std::vector const */*it*/, std::vector */*unselected_nodes*/) const { - if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false) { + if (!(_snap_enabled && _snapmanager->snapprefs.getSnapFrom(t)) ) { return; } diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 35841891f..f5021e208 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -44,9 +44,7 @@ Inkscape::SnapCandidate::~SnapCandidate() } Inkscape::ObjectSnapper::ObjectSnapper(SnapManager *sm, Geom::Coord const d) - : Snapper(sm, d), _snap_to_itemnode(true), _snap_to_itempath(true), - _snap_to_bboxnode(true), _snap_to_bboxpath(true), _snap_to_page_border(false), - _strict_snapping(true) + : Snapper(sm, d) { _candidates = new std::vector; _points_to_snap_to = new std::vector; @@ -176,7 +174,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType // A point considered for snapping should be either a node, a bbox corner or a guide. Pick only ONE! g_assert(!((p_is_a_node && p_is_a_bbox) || (p_is_a_bbox && p_is_a_guide) || (p_is_a_node && p_is_a_guide))); - if (_snap_to_bboxnode) { + if (_snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints()) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool prefs_bbox = prefs->getBool("/tools/bounding_box"); bbox_type = !prefs_bbox ? @@ -184,7 +182,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType } // Consider the page border for snapping - if (_snap_to_page_border) { + if (_snapmanager->snapprefs.getSnapToPageBorder()) { _getBorderNodes(_points_to_snap_to); } @@ -197,51 +195,47 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType g_return_if_fail(root_item); //Collect all nodes so we can snap to them - if (_snap_to_itemnode) { - if (!(_strict_snapping && !p_is_a_node) || p_is_a_guide) { - // Note: there are two ways in which intersections are considered: - // Method 1: Intersections are calculated for each shape individually, for both the - // snap source and snap target (see sp_shape_snappoints) - // Method 2: Intersections are calculated for each curve or line that we've snapped to, i.e. only for - // the target (see the intersect() method in the SnappedCurve and SnappedLine classes) - // Some differences: - // - Method 1 doesn't find intersections within a set of multiple objects - // - Method 2 only works for targets - // When considering intersections as snap targets: - // - Method 1 only works when snapping to nodes, whereas - // - Method 2 only works when snapping to paths - // - There will be performance differences too! - // If both methods are being used simultaneously, then this might lead to duplicate targets! - - // Well, here we will be looking for snap TARGETS. Both methods can therefore be used. - // When snapping to paths, we will get a collection of snapped lines and snapped curves. findBestSnap() will - // go hunting for intersections (but only when asked to in the prefs of course). In that case we can just - // temporarily block the intersections in sp_item_snappoints, we don't need duplicates. If we're not snapping to - // paths though but only to item nodes then we should still look for the intersections in sp_item_snappoints() - bool old_pref = _snapmanager->snapprefs.getSnapIntersectionCS(); - if (_snap_to_itempath) { - _snapmanager->snapprefs.setSnapIntersectionCS(false); - } - - sp_item_snappoints(root_item, SnapPointsIter(*_points_to_snap_to), &_snapmanager->snapprefs); - - if (_snap_to_itempath) { - _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); - } - } - } + if (p_is_a_node || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node) || p_is_a_guide) { + // Note: there are two ways in which intersections are considered: + // Method 1: Intersections are calculated for each shape individually, for both the + // snap source and snap target (see sp_shape_snappoints) + // Method 2: Intersections are calculated for each curve or line that we've snapped to, i.e. only for + // the target (see the intersect() method in the SnappedCurve and SnappedLine classes) + // Some differences: + // - Method 1 doesn't find intersections within a set of multiple objects + // - Method 2 only works for targets + // When considering intersections as snap targets: + // - Method 1 only works when snapping to nodes, whereas + // - Method 2 only works when snapping to paths + // - There will be performance differences too! + // If both methods are being used simultaneously, then this might lead to duplicate targets! + + // Well, here we will be looking for snap TARGETS. Both methods can therefore be used. + // When snapping to paths, we will get a collection of snapped lines and snapped curves. findBestSnap() will + // go hunting for intersections (but only when asked to in the prefs of course). In that case we can just + // temporarily block the intersections in sp_item_snappoints, we don't need duplicates. If we're not snapping to + // paths though but only to item nodes then we should still look for the intersections in sp_item_snappoints() + bool old_pref = _snapmanager->snapprefs.getSnapIntersectionCS(); + if (_snapmanager->snapprefs.getSnapToItemPath()) { + _snapmanager->snapprefs.setSnapIntersectionCS(false); + } + + sp_item_snappoints(root_item, SnapPointsIter(*_points_to_snap_to), &_snapmanager->snapprefs); + + if (_snapmanager->snapprefs.getSnapToItemPath()) { + _snapmanager->snapprefs.setSnapIntersectionCS(old_pref); + } + } //Collect the bounding box's corners so we can snap to them - if (_snap_to_bboxnode) { - if (!(_strict_snapping && !p_is_a_bbox) || p_is_a_guide) { - // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox - // of the item AND the bbox of the clipping path at the same time - if (!(*i).clip_or_mask) { - Geom::OptRect b = sp_item_bbox_desktop(root_item, bbox_type); - getBBoxPoints(b, _points_to_snap_to, _snapmanager->snapprefs.getSnapMidpoints()); - } - } - } + if (p_is_a_bbox || !(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_bbox) || p_is_a_guide) { + // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox + // of the item AND the bbox of the clipping path at the same time + if (!(*i).clip_or_mask) { + Geom::OptRect b = sp_item_bbox_desktop(root_item, bbox_type); + getBBoxPoints(b, _points_to_snap_to, _snapmanager->snapprefs.getSnapToBBoxNode(), _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints(), _snapmanager->snapprefs.getSnapBBoxMidpoints()); + } + } } } } @@ -324,7 +318,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType bool p_is_a_node = t & Inkscape::SnapPreferences::SNAPPOINT_NODE; - if (_snap_to_bboxpath) { + if (_snapmanager->snapprefs.getSnapToBBoxPath()) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int prefs_bbox = prefs->getBool("/tools/bounding_box", 0); bbox_type = !prefs_bbox ? @@ -332,7 +326,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType } // Consider the page border for snapping - if (_snap_to_page_border) { + if (_snapmanager->snapprefs.getSnapToPageBorder()) { Geom::PathVector *border_path = _getBorderPathv(); if (border_path != NULL) { _paths_to_snap_to->push_back(border_path); @@ -357,8 +351,8 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType //Build a list of all paths considered for snapping to //Add the item's path to snap to - if (_snap_to_itempath) { - if (!(_strict_snapping && !p_is_a_node)) { + if (_snapmanager->snapprefs.getSnapToItemPath()) { + if (!(_snapmanager->snapprefs.getStrictSnapping() && !p_is_a_node)) { // Snapping to the path of characters is very cool, but for a large // chunk of text this will take ages! So limit snapping to text paths // containing max. 240 characters. Snapping the bbox will not be affected @@ -391,8 +385,8 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType } //Add the item's bounding box to snap to - if (_snap_to_bboxpath) { - if (!(_strict_snapping && p_is_a_node)) { + if (_snapmanager->snapprefs.getSnapToBBoxPath()) { + if (!(_snapmanager->snapprefs.getStrictSnapping() && p_is_a_node)) { // Discard the bbox of a clipped path / mask, because we don't want to snap to both the bbox // of the item AND the bbox of the clipping path at the same time if (!(*i).clip_or_mask) { @@ -422,7 +416,7 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, g_assert(_snapmanager->getDesktop() != NULL); Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p); - bool const node_tool_active = _snap_to_itempath && selected_path != NULL; + bool const node_tool_active = _snapmanager->snapprefs.getSnapToItemPath() && selected_path != NULL; if (first_point) { /* findCandidates() is used for snapping to both paths and nodes. It ignores the path that is @@ -591,11 +585,15 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, _findCandidates(sp_document_root(_snapmanager->getDocument()), it, first_point, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); } - if (_snap_to_itemnode || _snap_to_bboxnode || _snap_to_page_border) { + if (_snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapSmoothNodes() + || _snapmanager->snapprefs.getSnapToBBoxNode() || _snapmanager->snapprefs.getSnapToPageBorder() + || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() + || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() + || _snapmanager->snapprefs.getIncludeItemCenter()) { _snapNodes(sc, t, p, first_point, unselected_nodes); } - if (_snap_to_itempath || _snap_to_bboxpath || _snap_to_page_border) { + if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { unsigned n = (unselected_nodes == NULL) ? 0 : unselected_nodes->size(); if (n > 0) { /* While editing a path in the node tool, findCandidates must ignore that path because @@ -643,7 +641,7 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, // to objects we will only consider the object's paths. Beside, the nodes will be at these paths, // so we will more or less snap to them anyhow. - if (_snap_to_itempath || _snap_to_bboxpath || _snap_to_page_border) { + if (_snapmanager->snapprefs.getSnapToItemPath() || _snapmanager->snapprefs.getSnapToBBoxPath() || _snapmanager->snapprefs.getSnapToPageBorder()) { _snapPathsConstrained(sc, t, p, first_point, c); } } @@ -687,13 +685,21 @@ void Inkscape::ObjectSnapper::guideSnap(SnappedConstraints &sc, */ bool Inkscape::ObjectSnapper::ThisSnapperMightSnap() const { - bool snap_to_something = _snap_to_itempath || _snap_to_itemnode || _snap_to_bboxpath || _snap_to_bboxnode || _snap_to_page_border; + bool snap_to_something = _snapmanager->snapprefs.getSnapToItemPath() + || _snapmanager->snapprefs.getSnapToItemNode() + || _snapmanager->snapprefs.getSnapToBBoxPath() + || _snapmanager->snapprefs.getSnapToBBoxNode() + || _snapmanager->snapprefs.getSnapToPageBorder() + || _snapmanager->snapprefs.getSnapLineMidpoints() || _snapmanager->snapprefs.getSnapObjectMidpoints() + || _snapmanager->snapprefs.getSnapBBoxEdgeMidpoints() || _snapmanager->snapprefs.getSnapBBoxMidpoints() + || _snapmanager->snapprefs.getIncludeItemCenter(); + return (_snap_enabled && _snapmanager->snapprefs.getSnapModeBBoxOrNodes() && snap_to_something); } bool Inkscape::ObjectSnapper::GuidesMightSnap() const { - bool snap_to_something = _snap_to_itemnode || _snap_to_bboxnode; + bool snap_to_something = _snapmanager->snapprefs.getSnapToItemNode() || _snapmanager->snapprefs.getSnapToBBoxNode(); return (_snap_enabled && _snapmanager->snapprefs.getSnapModeGuide() && snap_to_something); } @@ -732,17 +738,22 @@ void Inkscape::ObjectSnapper::_getBorderNodes(std::vector *points) points->push_back(Geom::Point(w,0)); } -void Inkscape::getBBoxPoints(Geom::OptRect const bbox, std::vector *points, bool const includeMidpoints) +void Inkscape::getBBoxPoints(Geom::OptRect const bbox, std::vector *points, bool const includeCorners, bool const includeLineMidpoints, bool const includeObjectMidpoints) { if (bbox) { // collect the corners of the bounding box for ( unsigned k = 0 ; k < 4 ; k++ ) { - points->push_back(bbox->corner(k)); + if (includeCorners) { + points->push_back(bbox->corner(k)); + } // optionally, collect the midpoints of the bounding box's edges too - if (includeMidpoints) { + if (includeLineMidpoints) { points->push_back((bbox->corner(k) + bbox->corner((k+1) % 4))/2); } } + if (includeObjectMidpoints) { + points->push_back(bbox->midpoint()); + } } } diff --git a/src/object-snapper.h b/src/object-snapper.h index 9151dbd8a..9c391a90f 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -56,16 +56,6 @@ public: ANGLED_GUIDE_ROT_SNAP, // For snapping an angled guide, while rotating it around some pivot point TRANSL_SNAP_XY}; // All other cases; for snapping to objects, other than guides - void setSnapToItemNode(bool s) {_snap_to_itemnode = s;} - bool getSnapToItemNode() const {return _snap_to_itemnode;} - void setSnapToItemPath(bool s) {_snap_to_itempath = s;} - bool getSnapToItemPath() const {return _snap_to_itempath;} - void setSnapToBBoxNode(bool s) {_snap_to_bboxnode = s;} - bool getSnapToBBoxNode() const {return _snap_to_bboxnode;} - void setSnapToBBoxPath(bool s) {_snap_to_bboxpath = s;} - bool getSnapToBBoxPath() const {return _snap_to_bboxpath;} - void setSnapToPageBorder(bool s) {_snap_to_page_border = s;} - bool getSnapToPageBorder() const {return _snap_to_page_border;} void guideSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal) const; @@ -140,20 +130,9 @@ private: Geom::PathVector* _getPathvFromRect(Geom::Rect const rect) const; void _getBorderNodes(std::vector *points) const; - bool _snap_to_itemnode; - bool _snap_to_itempath; - bool _snap_to_bboxnode; - bool _snap_to_bboxpath; - bool _snap_to_page_border; - - //If enabled, then bbox corners will only snap to bboxes, - //and nodes will only snap to nodes and paths. We will not - //snap bbox corners to nodes, or nodes to bboxes. - //(snapping to grids and guides is not affected by this) - bool _strict_snapping; }; // end of ObjectSnapper class -void getBBoxPoints(Geom::OptRect const bbox, std::vector *points, bool const includeMidpoints); +void getBBoxPoints(Geom::OptRect const bbox, std::vector *points, bool const includeCorners, bool const includeLineMidpoints, bool const includeObjectMidpoints); } // end of namespace Inkscape diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 1055e55ef..0fd15593c 100644 --- 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 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 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,7 +316,9 @@ void Inkscape::SelTrans::grab(Geom::Point const &p, gdouble x, gdouble y, bool s _bbox_points.clear(); if (_bbox) { - getBBoxPoints(_bbox, &_bbox_points, m.snapprefs.getSnapMidpoints()); + 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 diff --git a/src/snap-preferences.cpp b/src/snap-preferences.cpp index 0bfd94c71..d21259983 100644 --- a/src/snap-preferences.cpp +++ b/src/snap-preferences.cpp @@ -23,18 +23,22 @@ Inkscape::SnapPreferences::PointType const Inkscape::SnapPreferences::SNAPPOINT_ Inkscape::SnapPreferences::SnapPreferences() : _include_item_center(false), _snap_enabled_globally(true), - _snap_postponed_globally(false) + _snap_postponed_globally(false), + _snap_to_itemnode(true), _snap_to_itempath(true), + _snap_to_bboxnode(true), _snap_to_bboxpath(true), + _snap_to_page_border(false), + _strict_snapping(true) { setSnapFrom(SNAPPOINT_BBOX | SNAPPOINT_NODE, true); //Snap any point. In v0.45 and earlier, this was controlled in the preferences tab } /* * The snappers have too many parameters to adjust individually. Therefore only - * two snapping modes are presented to the user: snapping bounding box corners (to + * two snapping modes are presented to the user: snapping bounding box corners (to * other bounding boxes, grids or guides), and/or snapping nodes (to other nodes, - * paths, grids or guides). To select either of these modes (or both), use the + * paths, grids or guides). To select either of these modes (or both), use the * methods defined below: setSnapModeBBox() and setSnapModeNode(). - * + * * */ diff --git a/src/snap-preferences.h b/src/snap-preferences.h index a52e0cfd9..97ef3cec2 100644 --- a/src/snap-preferences.h +++ b/src/snap-preferences.h @@ -39,11 +39,17 @@ public: void setSnapIntersectionGG(bool enabled) {_intersectionGG = enabled;} void setSnapIntersectionCS(bool enabled) {_intersectionCS = enabled;} void setSnapSmoothNodes(bool enabled) {_smoothNodes = enabled;} - void setSnapMidpoints(bool enabled) {_midpoints = enabled;} + void setSnapLineMidpoints(bool enabled) {_line_midpoints = enabled;} + void setSnapObjectMidpoints(bool enabled) {_object_midpoints = enabled;} + void setSnapBBoxEdgeMidpoints(bool enabled) {_bbox_edge_midpoints = enabled;} + void setSnapBBoxMidpoints(bool enabled) {_bbox_midpoints = enabled;} bool getSnapIntersectionGG() const {return _intersectionGG;} bool getSnapIntersectionCS() const {return _intersectionCS;} bool getSnapSmoothNodes() const {return _smoothNodes;} - bool getSnapMidpoints() const {return _midpoints;} + bool getSnapLineMidpoints() const {return _line_midpoints;} + bool getSnapObjectMidpoints() const {return _object_midpoints;} + bool getSnapBBoxEdgeMidpoints() const {return _bbox_edge_midpoints;} + bool getSnapBBoxMidpoints() const {return _bbox_midpoints;} void setIncludeItemCenter(bool enabled) {_include_item_center = enabled;} bool getIncludeItemCenter() const {return _include_item_center;} @@ -57,16 +63,43 @@ public: void setSnapFrom(PointType t, bool s); bool getSnapFrom(PointType t) const; + // These will only be used for the object snapper + void setSnapToItemNode(bool s) {_snap_to_itemnode = s;} + bool getSnapToItemNode() const {return _snap_to_itemnode;} + void setSnapToItemPath(bool s) {_snap_to_itempath = s;} + bool getSnapToItemPath() const {return _snap_to_itempath;} + void setSnapToBBoxNode(bool s) {_snap_to_bboxnode = s;} + bool getSnapToBBoxNode() const {return _snap_to_bboxnode;} + void setSnapToBBoxPath(bool s) {_snap_to_bboxpath = s;} + bool getSnapToBBoxPath() const {return _snap_to_bboxpath;} + void setSnapToPageBorder(bool s) {_snap_to_page_border = s;} + bool getSnapToPageBorder() const {return _snap_to_page_border;} + bool getStrictSnapping() const {return _strict_snapping;} + private: bool _include_item_center; //If true, snapping nodes will also snap the item's center bool _intersectionGG; //Consider snapping to intersections of grid and guides bool _intersectionCS; //Consider snapping to intersections of curves bool _smoothNodes; - bool _midpoints; + bool _line_midpoints; + bool _object_midpoints; // the midpoint of shapes (e.g. a circle, rect, polygon) or of any other shape (at [h/2, w/2]) + bool _bbox_edge_midpoints; + bool _bbox_midpoints; bool _snap_enabled_globally; // Toggles ALL snapping bool _snap_postponed_globally; // Hold all snapping temporarily when the mouse is moving fast PointType _snap_from; ///< bitmap of point types that we will snap from + // These will only be used for the object snapper + bool _snap_to_itemnode; + bool _snap_to_itempath; + bool _snap_to_bboxnode; + bool _snap_to_bboxpath; + bool _snap_to_page_border; + //If enabled, then bbox corners will only snap to bboxes, + //and nodes will only snap to nodes and paths. We will not + //snap bbox corners to nodes, or nodes to bboxes. + //(snapping to grids and guides is not affected by this) + bool _strict_snapping; }; } diff --git a/src/snap.cpp b/src/snap.cpp index a6c4b8729..24cfefe0b 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -107,6 +107,25 @@ bool SnapManager::someSnapperMightSnap() const return (i != s.end()); } +/** + * \return true if one of the snappers will try to snap something. + */ + +bool SnapManager::gridSnapperMightSnap() const +{ + if ( !snapprefs.getSnapEnabledGlobally() || snapprefs.getSnapPostponedGlobally() ) { + return false; + } + + SnapperList const s = getGridSnappers(); + SnapperList::const_iterator i = s.begin(); + while (i != s.end() && (*i)->ThisSnapperMightSnap() == false) { + i++; + } + + return (i != s.end()); +} + /** * Try to snap a point to any of the specified snappers. * diff --git a/src/snap.h b/src/snap.h index 14adf399f..177e9b529 100644 --- a/src/snap.h +++ b/src/snap.h @@ -54,6 +54,7 @@ public: typedef std::list SnapperList; bool someSnapperMightSnap() const; + bool gridSnapperMightSnap() const; void setup(SPDesktop const *desktop, bool snapindicator = true, SPItem const *item_to_ignore = NULL, std::vector *unselected_nodes = NULL); void setup(SPDesktop const *desktop, bool snapindicator, std::vector &items_to_ignore, std::vector *unselected_nodes = NULL); diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp index c7ed88513..0d28e9f6b 100644 --- a/src/sp-ellipse.cpp +++ b/src/sp-ellipse.cpp @@ -257,16 +257,21 @@ static void sp_genericellipse_set_shape(SPShape *shape) curve->unref(); } -static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs) { g_assert(item != NULL); g_assert(SP_IS_GENERICELLIPSE(item)); + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + SPGenericEllipse *ellipse = SP_GENERICELLIPSE(item); sp_genericellipse_normalize(ellipse); Geom::Matrix const i2d = sp_item_i2d_affine(item); - // figure out if we have a slice, whilst guarding against rounding errors + // figure out if we have a slice, while guarding against rounding errors bool slice = false; double len = fmod(ellipse->end - ellipse->start, SP_2PI); if (len < 0.0) len += SP_2PI; @@ -284,19 +289,22 @@ static void sp_genericellipse_snappoints(SPItem const *item, SnapPointsIter p, I // Snap to the 4 quadrant points of the ellipse, but only if the arc // spans far enough to include them - double angle = 0; - for (angle = 0; angle < SP_2PI; angle += M_PI_2) { - if (angle >= ellipse->start && angle <= ellipse->end) { - *p = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d; - } + if (snapprefs->getSnapToItemNode()) { //TODO: Make a separate snap option toggle for this? + double angle = 0; + for (angle = 0; angle < SP_2PI; angle += M_PI_2) { + if (angle >= ellipse->start && angle <= ellipse->end) { + *p = Geom::Point(cx + cos(angle)*rx, cy + sin(angle)*ry) * i2d; + } + } } - // And if we have a slice, also snap to the endpoints and the centre point - if (slice) { - // Add the centre, if we have a closed slice - if (ellipse->closed) { - *p = Geom::Point(cx, cy) * i2d; - } + // Add the centre, if we have a closed slice or when explicitly asked for + if ((snapprefs->getSnapToItemNode() && slice && ellipse->closed) || snapprefs->getSnapObjectMidpoints()) { + *p = Geom::Point(cx, cy) * i2d; + } + + // And if we have a slice, also snap to the endpoints + if (snapprefs->getSnapToItemNode() && slice) { // Add the start point, if it's not coincident with a quadrant point if (fmod(ellipse->start, M_PI_2) != 0.0 ) { *p = Geom::Point(cx + cos(ellipse->start)*rx, cy + sin(ellipse->start)*ry) * i2d; diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 8971c3abb..2ac480341 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -973,7 +973,7 @@ void sp_item_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPref // Get the snappoints at the item's center if (snapprefs != NULL && snapprefs->getIncludeItemCenter()) { - *p = item->getCenter(); + *p = item->getCenter(); } // Get the snappoints of clipping paths and mask, if any diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index c46f30e06..4ff66f6c3 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -249,7 +249,10 @@ static void sp_namedview_build(SPObject *object, SPDocument *document, Inkscape: sp_object_read_attr(object, "inkscape:snap-center"); sp_object_read_attr(object, "inkscape:snap-smooth-nodes"); sp_object_read_attr(object, "inkscape:snap-midpoints"); - sp_object_read_attr(object, "inkscape:snap-intersection-grid-guide"); + sp_object_read_attr(object, "inkscape:snap-object-midpoints"); + sp_object_read_attr(object, "inkscape:snap-bbox-edge-midpoints"); + sp_object_read_attr(object, "inkscape:snap-bbox-midpoints"); + sp_object_read_attr(object, "inkscape:snap-intersection-grid-guide"); sp_object_read_attr(object, "inkscape:snap-intersection-paths"); sp_object_read_attr(object, "inkscape:object-paths"); sp_object_read_attr(object, "inkscape:object-nodes"); @@ -471,11 +474,23 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va nv->snap_manager.snapprefs.setSnapSmoothNodes(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; - case SP_ATTR_INKSCAPE_SNAP_MIDPOINTS: - nv->snap_manager.snapprefs.setSnapMidpoints(value ? sp_str_to_bool(value) : FALSE); + case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS: + nv->snap_manager.snapprefs.setSnapLineMidpoints(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; - case SP_ATTR_INKSCAPE_SNAP_GUIDE: + case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS: + nv->snap_manager.snapprefs.setSnapObjectMidpoints(value ? sp_str_to_bool(value) : FALSE); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS: + nv->snap_manager.snapprefs.setSnapBBoxEdgeMidpoints(value ? sp_str_to_bool(value) : FALSE); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS: + nv->snap_manager.snapprefs.setSnapBBoxMidpoints(value ? sp_str_to_bool(value) : FALSE); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_SNAP_GUIDE: nv->snap_manager.snapprefs.setSnapModeGuide(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; @@ -488,23 +503,23 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_OBJECT_PATHS: - nv->snap_manager.object.setSnapToItemPath(value ? sp_str_to_bool(value) : FALSE); + nv->snap_manager.snapprefs.setSnapToItemPath(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_OBJECT_NODES: - nv->snap_manager.object.setSnapToItemNode(value ? sp_str_to_bool(value) : FALSE); + nv->snap_manager.snapprefs.setSnapToItemNode(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_BBOX_PATHS: - nv->snap_manager.object.setSnapToBBoxPath(value ? sp_str_to_bool(value) : FALSE); + nv->snap_manager.snapprefs.setSnapToBBoxPath(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_BBOX_NODES: - nv->snap_manager.object.setSnapToBBoxNode(value ? sp_str_to_bool(value) : FALSE); + nv->snap_manager.snapprefs.setSnapToBBoxNode(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_SNAP_PAGE: - nv->snap_manager.object.setSnapToPageBorder(value ? sp_str_to_bool(value) : FALSE); + nv->snap_manager.snapprefs.setSnapToPageBorder(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_CURRENT_LAYER: diff --git a/src/sp-rect.cpp b/src/sp-rect.cpp index 2f3fc1864..c2fe527b6 100644 --- a/src/sp-rect.cpp +++ b/src/sp-rect.cpp @@ -556,6 +556,11 @@ static void sp_rect_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::S g_assert(item != NULL); g_assert(SP_IS_RECT(item)); + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + SPRect *rect = SP_RECT(item); Geom::Matrix const i2d (sp_item_i2d_affine (item)); @@ -565,17 +570,24 @@ static void sp_rect_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::S Geom::Point p2 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed + rect->height.computed) * i2d; Geom::Point p3 = Geom::Point(rect->x.computed + rect->width.computed, rect->y.computed) * i2d; - *p = p0; - *p = p1; - *p = p2; - *p = p3; - - if (snapprefs->getSnapMidpoints()) { - *p = (p0 + p1)/2; - *p = (p1 + p2)/2; - *p = (p2 + p3)/2; - *p = (p3 + p0)/2; + if (snapprefs->getSnapToItemNode()) { + *p = p0; + *p = p1; + *p = p2; + *p = p3; } + + if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) + *p = (p0 + p1)/2; + *p = (p1 + p2)/2; + *p = (p2 + p3)/2; + *p = (p3 + p0)/2; + } + + if (snapprefs->getSnapObjectMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) + *p = (p0 + p2)/2; + } + } void diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index ce73888ed..4adf883a4 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -1067,14 +1067,28 @@ static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape:: return; } + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + Geom::PathVector const &pathv = shape->curve->get_pathvector(); if (pathv.empty()) return; Geom::Matrix const i2d (sp_item_i2d_affine (item)); + if (snapprefs->getSnapObjectMidpoints()) { + Geom::OptRect bbox = item->getBounds(sp_item_i2d_affine(item)); + if (bbox) { + *p = bbox->midpoint(); + } + } + for(Geom::PathVector::const_iterator path_it = pathv.begin(); path_it != pathv.end(); ++path_it) { - *p = path_it->initialPoint() * i2d; + if (snapprefs->getSnapToItemNode()) { + *p = path_it->initialPoint() * i2d; + } Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve @@ -1088,23 +1102,26 @@ static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape:: Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2); - // Depending on the snapping preferences, either add only cusp nodes, or add add both cusp and smooth nodes - if (snapprefs->getSnapSmoothNodes() || nodetype == Geom::NODE_NONE || nodetype == Geom::NODE_CUSP) { - *p = curve_it1->finalPoint() * i2d; - } + bool c1 = snapprefs->getSnapToItemNode() && (nodetype == Geom::NODE_CUSP || nodetype == Geom::NODE_NONE); + bool c2 = snapprefs->getSnapSmoothNodes() && (nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM); - // Consider midpoints of line segments for snapping - if (snapprefs->getSnapMidpoints()) { - if (Geom::LineSegment const* line_segment = dynamic_cast(&(*curve_it1))) { - *p = Geom::middle_point(*line_segment) * i2d; - } + if (c1 || c2) { + *p = curve_it1->finalPoint() * i2d; } + // Consider midpoints of line segments for snapping + if (snapprefs->getSnapLineMidpoints()) { // only do this when we're snapping nodes (enforce strict snapping) + if (Geom::LineSegment const* line_segment = dynamic_cast(&(*curve_it1))) { + *p = Geom::middle_point(*line_segment) * i2d; + } + } + ++curve_it1; ++curve_it2; } - // Find the internal intersections of each path and consider these for snapping (using "Method 1" as desciribed in Inkscape::ObjectSnapper::_collectNodes()) + // Find the internal intersections of each path and consider these for snapping + // (using "Method 1" as described in Inkscape::ObjectSnapper::_collectNodes()) if (snapprefs->getSnapIntersectionCS()) { Geom::Crossings cs; cs = self_crossings(*path_it); @@ -1117,8 +1134,6 @@ static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape:: } } - - } /* diff --git a/src/sp-spiral.cpp b/src/sp-spiral.cpp index 377cc13f4..624c5ae18 100644 --- a/src/sp-spiral.cpp +++ b/src/sp-spiral.cpp @@ -426,7 +426,7 @@ sp_spiral_set_shape (SPShape *shape) SP_OBJECT (spiral)->requestModified(SP_OBJECT_MODIFIED_FLAG); SPCurve *c = new SPCurve (); - + #ifdef SPIRAL_VERBOSE g_print ("cx=%g, cy=%g, exp=%g, revo=%g, rad=%g, arg=%g, t0=%g\n", spiral->cx, @@ -496,7 +496,7 @@ sp_spiral_position_set (SPSpiral *spiral, spiral->rad = MAX (rad, 0.001); spiral->arg = arg; spiral->t0 = CLAMP(t0, 0.0, 0.999); - + ((SPObject *)spiral)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -505,8 +505,26 @@ sp_spiral_position_set (SPSpiral *spiral, */ static void sp_spiral_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs) { + // We will determine the spiral's midpoint ourselves, instead of trusting on the base class + // Therefore setSnapObjectMidpoints() is set to false temporarily + Inkscape::SnapPreferences local_snapprefs = *snapprefs; + local_snapprefs.setSnapObjectMidpoints(false); + if (((SPItemClass *) parent_class)->snappoints) { - ((SPItemClass *) parent_class)->snappoints (item, p, snapprefs); + ((SPItemClass *) parent_class)->snappoints (item, p, &local_snapprefs); + } + + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + + if (snapprefs->getSnapObjectMidpoints()) { + Geom::Matrix const i2d (sp_item_i2d_affine (item)); + SPSpiral *spiral = SP_SPIRAL(item); + *p = Geom::Point(spiral->cx, spiral->cy) * i2d; + // This point is the start-point of the spiral, which is also returned when _snap_to_itemnode has been set + // in the object snapper. In that case we will get a duplicate! } } diff --git a/src/sp-star.cpp b/src/sp-star.cpp index bbf808209..c1581a6d6 100644 --- a/src/sp-star.cpp +++ b/src/sp-star.cpp @@ -429,7 +429,7 @@ sp_star_set_shape (SPShape *shape) SPStar *star = SP_STAR (shape); SPCurve *c = new SPCurve (); - + gint sides = star->sides; bool not_rounded = (fabs (star->rounded) < 1e-4); @@ -474,7 +474,7 @@ sp_star_set_shape (SPShape *shape) } } } - + // draw last segment if (not_rounded) { c->lineto(sp_star_get_xy (star, SP_STAR_POINT_KNOT1, 0, true)); @@ -511,7 +511,7 @@ sp_star_position_set (SPStar *star, gint sides, Geom::Point center, gdouble r1, { g_return_if_fail (star != NULL); g_return_if_fail (SP_IS_STAR (star)); - + star->sides = CLAMP (sides, 3, 1024); star->center = center; star->r[0] = MAX (r1, 0.001); @@ -528,12 +528,25 @@ sp_star_position_set (SPStar *star, gint sides, Geom::Point center, gdouble r1, SP_OBJECT(star)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } -/* fixme: We should use all corners of star (Lauris) */ - static void sp_star_snappoints(SPItem const *item, SnapPointsIter p, Inkscape::SnapPreferences const *snapprefs) { + // We will determine the star's midpoint ourselves, instead of trusting on the base class + // Therefore setSnapObjectMidpoints() is set to false temporarily + Inkscape::SnapPreferences local_snapprefs = *snapprefs; + local_snapprefs.setSnapObjectMidpoints(false); + if (((SPItemClass *) parent_class)->snappoints) { - ((SPItemClass *) parent_class)->snappoints (item, p, snapprefs); + ((SPItemClass *) parent_class)->snappoints (item, p, &local_snapprefs); + } + + // Help enforcing strict snapping, i.e. only return nodes when we're snapping nodes to nodes or a guide to nodes + if (!(snapprefs->getSnapModeNode() || snapprefs->getSnapModeGuide())) { + return; + } + + if (snapprefs->getSnapObjectMidpoints()) { + Geom::Matrix const i2d (sp_item_i2d_affine (item)); + *p = SP_STAR(item)->center * i2d; } } diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index b2b3d1e81..36861ad2c 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -875,14 +875,14 @@ DocumentProperties::update() _rcbsng.setActive (nv->snap_manager.snapprefs.getSnapModeGuide()); _rcbic.setActive (nv->snap_manager.snapprefs.getIncludeItemCenter()); _rcbsm.setActive (nv->snap_manager.snapprefs.getSnapSmoothNodes()); - _rcbmp.setActive (nv->snap_manager.snapprefs.getSnapMidpoints()); + _rcbmp.setActive (nv->snap_manager.snapprefs.getSnapLineMidpoints()); _rcbsigg.setActive (nv->snap_manager.snapprefs.getSnapIntersectionGG()); _rcbsils.setActive (nv->snap_manager.snapprefs.getSnapIntersectionCS()); - _rcbsnop.setActive(nv->snap_manager.object.getSnapToItemPath()); - _rcbsnon.setActive(nv->snap_manager.object.getSnapToItemNode()); - _rcbsnbbp.setActive(nv->snap_manager.object.getSnapToBBoxPath()); - _rcbsnbbn.setActive(nv->snap_manager.object.getSnapToBBoxNode()); - _rcbsnpb.setActive(nv->snap_manager.object.getSnapToPageBorder()); + _rcbsnop.setActive(nv->snap_manager.snapprefs.getSnapToItemPath()); + _rcbsnon.setActive(nv->snap_manager.snapprefs.getSnapToItemNode()); + _rcbsnbbp.setActive(nv->snap_manager.snapprefs.getSnapToBBoxPath()); + _rcbsnbbn.setActive(nv->snap_manager.snapprefs.getSnapToBBoxNode()); + _rcbsnpb.setActive(nv->snap_manager.snapprefs.getSnapToPageBorder()); _rsu_sno.setValue (nv->objecttolerance); _rsu_sn.setValue (nv->gridtolerance); diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp index 571e440f2..0acc7731a 100644 --- a/src/widgets/toolbox.cpp +++ b/src/widgets/toolbox.cpp @@ -1909,11 +1909,11 @@ void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v); break; case SP_ATTR_INKSCAPE_BBOX_PATHS: - v = nv->snap_manager.object.getSnapToBBoxPath(); + v = nv->snap_manager.snapprefs.getSnapToBBoxPath(); sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v); break; case SP_ATTR_INKSCAPE_BBOX_NODES: - v = nv->snap_manager.object.getSnapToBBoxNode(); + v = nv->snap_manager.snapprefs.getSnapToBBoxNode(); sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v); break; case SP_ATTR_INKSCAPE_SNAP_NODES: @@ -1921,21 +1921,17 @@ void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v); break; case SP_ATTR_INKSCAPE_OBJECT_PATHS: - v = nv->snap_manager.object.getSnapToItemPath(); + v = nv->snap_manager.snapprefs.getSnapToItemPath(); sp_repr_set_boolean(repr, "inkscape:object-paths", !v); break; case SP_ATTR_INKSCAPE_OBJECT_NODES: - v = nv->snap_manager.object.getSnapToItemNode(); + v = nv->snap_manager.snapprefs.getSnapToItemNode(); sp_repr_set_boolean(repr, "inkscape:object-nodes", !v); break; case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES: v = nv->snap_manager.snapprefs.getSnapSmoothNodes(); sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v); break; - case SP_ATTR_INKSCAPE_SNAP_MIDPOINTS: - v = nv->snap_manager.snapprefs.getSnapMidpoints(); - sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v); - break; case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS: v = nv->snap_manager.snapprefs.getSnapIntersectionCS(); sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v); @@ -1945,13 +1941,29 @@ void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points sp_repr_set_boolean(repr, "inkscape:snap-center", !v); break; case SP_ATTR_INKSCAPE_SNAP_PAGE: - v = nv->snap_manager.object.getSnapToPageBorder(); + v = nv->snap_manager.snapprefs.getSnapToPageBorder(); sp_repr_set_boolean(repr, "inkscape:snap-page", !v); break; case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE: v = nv->snap_manager.snapprefs.getSnapIntersectionGG(); sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v); break; + case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapLineMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapObjectMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v); + break; + case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS: + v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints(); + sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v); + break; default: g_warning("toggle_snap_callback has been called with an ID for which no action has been defined"); break; @@ -1975,17 +1987,20 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) " " " " " " + " " + " " " " " " " " + " " " " " " - " " + " " + " " " " " " " " " " - " " " " ""; @@ -1994,7 +2009,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal", // "name" _("Snap"), // "label" - _("Toggle all snapping"), // "tooltip" + _("Enable snapping"), // "tooltip" "toggle_snap_global", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_GLOBAL); @@ -2006,7 +2021,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",// "name" _("Bounding box"), // "label" - _("Toggle snapping of bounding box corners"), // "tooltip" + _("Snap bounding box corners"), // "tooltip" "toggle_snap_bbox", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX); @@ -2018,7 +2033,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath", // "name" _("Bounding box edges"), // "label" - _("Toggle snapping to edges of a bounding box"), // "tooltip" + _("Snap to edges of a bounding box"), // "tooltip" "toggle_snap_to_bbox_path", // "iconId" secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS); @@ -2030,7 +2045,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode", // "name" _("Bounding box corners"), // "label" - _("Toggle snapping to bounding box corners"), // "tooltip" + _("Snap to bounding box corners"), // "tooltip" "toggle_snap_to_bbox_node", // "iconId" secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES); @@ -2039,10 +2054,34 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); } + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints", // "name" + _("BBox Edge Midpoints"), // "label" + _("Snap from and to midpoints of bounding box edges"), // "tooltip" + "toggle_snap_to_bbox_edge_midpoints", // "iconId" + secondarySize, + SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxMidpoints", // "name" + _("BBox Midpoints"), // "label" + _("Snapping from and to midpoints of bounding boxes"), // "tooltip" + "toggle_snap_to_bbox_midpoints", // "iconId" + secondarySize, + SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + { InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode", // "name" _("Nodes"), // "label" - _("Toggle snapping of nodes"), // "tooltip" + _("Snap nodes"), // "tooltip" "toggle_snap_nodes", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES); @@ -2054,7 +2093,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath", // "name" _("Paths"), // "label" - _("Toggle snapping to paths"), // "tooltip" + _("Snap to paths"), // "tooltip" "toggle_snap_to_paths", // "iconId" secondarySize, SP_ATTR_INKSCAPE_OBJECT_PATHS); @@ -2063,10 +2102,22 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); } + { + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections", // "name" + _("Path intersections"), // "label" + _("Snap to path intersections"), // "tooltip" + "toggle_snap_to_path_intersections", // "iconId" + secondarySize, + SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS); + + gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); + g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); + } + { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode", // "name" _("To nodes"), // "label" - _("Toggle snapping to cusp nodes"), // "tooltip" + _("Snap to cusp nodes"), // "tooltip" "toggle_snap_to_nodes", // "iconId" secondarySize, SP_ATTR_INKSCAPE_OBJECT_NODES); @@ -2078,7 +2129,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes", // "name" _("Smooth nodes"), // "label" - _("Consider smooth nodes too, not just cusp nodes"),// "tooltip" + _("Snap to smooth nodes"),// "tooltip" "toggle_snap_to_smooth_nodes", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES); @@ -2088,24 +2139,24 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) } { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromMidpoints", // "name" - _("Midpoints"), // "label" - _("Toggle snapping from and to midpoints of line segments and bounding boxes"), // "tooltip" + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints", // "name" + _("Line Midpoints"), // "label" + _("Snap from and to midpoints of line segments"), // "tooltip" "toggle_snap_to_midpoints", // "iconId" secondarySize, - SP_ATTR_INKSCAPE_SNAP_MIDPOINTS); + SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS); gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); } { - InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections", // "name" - _("Path intersections"), // "label" - _("Toggle snapping to path intersections"), // "tooltip" - "toggle_snap_to_path_intersections", // "iconId" + InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectMidpoints", // "name" + _("Object Midpoints"), // "label" + _("Snap from and to midpoints of objects"), // "tooltip" + "toggle_snap_to_object_midpoints", // "iconId" secondarySize, - SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS); + SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS); gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) ); g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox ); @@ -2114,7 +2165,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromCenter",// "name" _("Center"), // "label" - _("Toggle snapping from and to an item's rotation center"), // "tooltip" + _("Snap from and to an item's rotation center"), // "tooltip" "toggle_snap_center", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER); @@ -2126,7 +2177,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder", // "name" _("Page border"), // "label" - _("Toggle snapping to the page border"), // "tooltip" + _("Snap to the page border"), // "tooltip" "toggle_snap_page_border", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE); @@ -2138,7 +2189,7 @@ void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop) { InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections", // "name" _("Grid/guide intersections"), // "label" - _("Toggle snapping to intersections of a grid with a guide"), // "tooltip" + _("Snap to intersections of a grid with a guide"), // "tooltip" "toggle_snap_grid_guide_intersections", // "iconId" secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE); @@ -2192,15 +2243,18 @@ void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, G Glib::RefPtr act2 = mainActions->get_action("ToggleSnapFromBBoxCorner"); Glib::RefPtr act3 = mainActions->get_action("ToggleSnapToBBoxPath"); Glib::RefPtr act4 = mainActions->get_action("ToggleSnapToBBoxNode"); + Glib::RefPtr act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints"); + Glib::RefPtr act4c = mainActions->get_action("ToggleSnapToFromBBoxMidpoints"); Glib::RefPtr act5 = mainActions->get_action("ToggleSnapFromNode"); Glib::RefPtr act6 = mainActions->get_action("ToggleSnapToItemPath"); + Glib::RefPtr act6b = mainActions->get_action("ToggleSnapToPathIntersections"); Glib::RefPtr act7 = mainActions->get_action("ToggleSnapToItemNode"); Glib::RefPtr act8 = mainActions->get_action("ToggleSnapToSmoothNodes"); - Glib::RefPtr act10 = mainActions->get_action("ToggleSnapToPathIntersections"); + Glib::RefPtr act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints"); + Glib::RefPtr act10 = mainActions->get_action("ToggleSnapToFromObjectMidpoints"); Glib::RefPtr act11 = mainActions->get_action("ToggleSnapToFromCenter"); Glib::RefPtr act12 = mainActions->get_action("ToggleSnapToPageBorder"); Glib::RefPtr act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections"); - Glib::RefPtr act14 = mainActions->get_action("ToggleSnapToFromMidpoints"); if (!act1) { @@ -2219,34 +2273,39 @@ void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, G gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2); gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.object.getSnapToBBoxPath()); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath()); gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.object.getSnapToBBoxNode()); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode()); gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2); bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode(); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3); gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), nv->snap_manager.object.getSnapToItemPath()); + bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath(); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4); gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3); - bool const c4 = nv->snap_manager.object.getSnapToItemNode(); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), c4); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS()); + gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode()); gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes()); - gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3 && c4); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS()); + gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints()); + gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints()); gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter()); gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.object.getSnapToPageBorder()); + gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder()); gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG()); gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1); - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapMidpoints()); - gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1); - g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above) }