object-snapper.cpp revision aee6bba4d090adbd7801efd2eb156ca8aee2301f
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * \brief Snapping things to objects.
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * Carl Hetherington <inkscape@carlh.net>
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * Diederik van Lierop <mail@diedenrezi.nl>
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * Copyright (C) 2005 - 2008 Authors
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * Released under GNU GPL, read the file 'COPYING' for more information
17eb9e57e1550f744916bf486947162aa523bddamentalInkscape::SnapCandidate::SnapCandidate(SPItem* item, bool clip_or_mask, NR::Matrix additional_affine)
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental : item(item), clip_or_mask(clip_or_mask), additional_affine(additional_affine)
526c8bf9bb41b582dc49f54ac192705de9e2edf2mentalInkscape::ObjectSnapper::ObjectSnapper(SPNamedView const *nv, NR::Coord const d)
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental : Snapper(nv, d), _snap_to_itemnode(true), _snap_to_itempath(true),
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental _snap_to_bboxnode(true), _snap_to_bboxpath(true), _snap_to_page_border(false),
9982f495cde2750b1c6446bfb152af73ab981512Johan Engelen _strict_snapping(true), _include_item_center(false)
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental _paths_to_snap_to = new std::vector<Geom::PathVector*>;
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * Find all items within snapping range.
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * \param parent Pointer to the document's root, or to a clipped path or mask object
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * \param it List of items to ignore
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * \param first_point If true then this point is the first one from a whole bunch of points
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * \param bbox_to_snap Bounding box hulling the whole bunch of points, all from the same selection and having the same transformation
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental * \param DimensionToSnap Snap in X, Y, or both directions.
526c8bf9bb41b582dc49f54ac192705de9e2edf2mentalvoid Inkscape::ObjectSnapper::_findCandidates(SPObject* parent,
526c8bf9bb41b582dc49f54ac192705de9e2edf2mental NR::Matrix const additional_affine) const // transformation of the item being clipped / masked
if (first_point) {
if (SP_IS_ITEM(o) && !SP_ITEM(o)->isLocked() && !(desktop->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) {
if (item) {
if (obj) {
if (obj) {
if (SP_IS_GROUP(o)) {
if (clip_or_mask) {
// insert an additional transformation in document coordinates (code copied from sp_item_i2d_affine)
if (bbox_of_item) {
bool const &first_point) const
if (first_point) {
// 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 (_snap_to_page_border) {
for (std::vector<SnapCandidate>::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) {
if (_snap_to_itemnode) {
if (_snap_to_bboxnode) {
if (!(*i).clip_or_mask) {
bool const &first_point,
_points_to_snap_to->insert(_points_to_snap_to->end(), unselected_nodes->begin(), unselected_nodes->end());
SnappedPoint s;
bool success = false;
for (std::vector<NR::Point>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) {
success = true;
if (success) {
_collectNodes(t, true);
SnappedPoint s;
bool success = false;
for (std::vector<NR::Point>::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) {
NR::Coord dist2 = NR::L2(p - p_proj); // distance from projection of node on the guide, to the mouse location
success = true;
if (success) {
bool const &first_point) const
if (first_point) {
_clear_paths();
if (_snap_to_bboxpath) {
if (_snap_to_page_border) {
for (std::vector<SnapCandidate>::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) {
if (_snap_to_itempath) {
bool very_lenghty_prose = false;
bool very_complex_path = false;
if (curve) {
Geom::PathVector *borderpathv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine);
_paths_to_snap_to->push_back(borderpathv); // Perhaps for speed, get a reference to the Geom::pathvector, and store the transformation besides it.
if (_snap_to_bboxpath) {
if (!(*i).clip_or_mask) {
bool const &first_point,
if (first_point) {
if (node_tool_active) {
if (curve) {
Geom::PathVector *pathv = pathvector_for_curve(SP_ITEM(selected_path), curve, true, true, Geom::identity(), Geom::identity()); // We will get our own copy of the path, which must be freed at some point
for (std::vector<Geom::PathVector*>::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) {
bool c1 = true;
bool c2 = true;
if (being_edited) {
sc.curves.push_back(Inkscape::SnappedCurve(from_2geom(sp_dt), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), curve));
bool Inkscape::ObjectSnapper::isUnselectedNode(NR::Point const &point, std::vector<NR::Point> const *unselected_nodes) const
for (std::vector<NR::Point>::const_iterator i = unselected_nodes->begin(); i != unselected_nodes->end(); i++) {
bool const &first_point,
ConstraintLine const &c) const
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);
for (std::vector<Geom::PathVector*>::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) {
SnappedPoint s(desktop->doc2dt(p_inters), SNAPTARGET_PATH, dist, getSnapperTolerance(), getSnapperAlwaysSnap());
bool const &first_point,
if (first_point) {
_findCandidates(sp_document_root(_named_view->document), it, first_point, local_bbox_to_snap, TRANSL_SNAP_XY, false, NR::identity());
bool const &first_point,
ConstraintLine const &c,
if (first_point) {
_findCandidates(sp_document_root(_named_view->document), it, first_point, local_bbox_to_snap, TRANSL_SNAP_XY, false, NR::identity());
_findCandidates(sp_document_root(_named_view->document), &it, true, NR::Rect(p, p), snap_dim, false, NR::identity());
bool snap_to_something = _snap_to_itempath || _snap_to_itemnode || _snap_to_bboxpath || _snap_to_bboxnode || _snap_to_page_border;
for (std::vector<Geom::PathVector*>::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) {
g_free(*k);
Geom::Rect const border_rect = Geom::Rect(Geom::Point(0,0), Geom::Point(sp_document_width(_named_view->document),sp_document_height(_named_view->document)));
if (border_curve) {
return dummy;
return NULL;