From 2b7ee786ac3f40bb6b0d6e19f565b802f29f50e0 Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Sun, 14 Dec 2008 20:49:00 +0000 Subject: [PATCH] 1) snap midpoints of line segments (both as source and as target) 2) snap intersections within a single shape (as source; as target was already implemented) --- src/attributes-test.h | 1 + src/attributes.cpp | 1 + src/attributes.h | 1 + src/display/canvas-axonomgrid.cpp | 54 ++--- src/display/canvas-axonomgrid.h | 18 +- src/display/canvas-grid.cpp | 32 +-- src/display/canvas-grid.h | 6 +- src/guide-snapper.cpp | 4 +- src/guide-snapper.h | 6 +- src/line-snapper.cpp | 32 +-- src/line-snapper.h | 10 +- src/object-snapper.cpp | 292 ++++++++++++++------------ src/object-snapper.h | 58 ++--- src/snap-preferences.h | 27 +-- src/snap.h | 52 ++--- src/snapped-curve.cpp | 2 +- src/snapper.cpp | 4 +- src/snapper.h | 12 +- src/sp-namedview.cpp | 11 +- src/sp-shape.cpp | 289 +++++++++++++------------ src/ui/dialog/document-properties.cpp | 7 +- src/ui/dialog/document-properties.h | 2 +- 22 files changed, 493 insertions(+), 428 deletions(-) diff --git a/src/attributes-test.h b/src/attributes-test.h index bffc27798..af1ed8a11 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -352,6 +352,7 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"inkscape:snap-guide", true}, {"inkscape:snap-center", true}, {"inkscape:snap-smooth-nodes", true}, + {"inkscape:snap-midpoints", true}, {"inkscape:snap-intersection-grid-guide", true}, {"inkscape:snap-intersection-paths", true}, {"inkscape:pageopacity", true}, diff --git a/src/attributes.cpp b/src/attributes.cpp index 149d177f5..1ee33ef83 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -92,6 +92,7 @@ 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_INTERS_PATHS, "inkscape:snap-intersection-paths"}, {SP_ATTR_INKSCAPE_OBJECT_PATHS, "inkscape:object-paths"}, diff --git a/src/attributes.h b/src/attributes.h index f8925f47c..68e6135db 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -92,6 +92,7 @@ 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_INTERS_GRIDGUIDE, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS, SP_ATTR_INKSCAPE_OBJECT_PATHS, diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index a636d8268..4b45eae33 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -19,7 +19,7 @@ #include "sp-canvas-util.h" #include "canvas-axonomgrid.h" -#include "util/mathfns.h" +#include "util/mathfns.h" #include "2geom/geom.h" #include "display-forward.h" #include @@ -402,12 +402,12 @@ _wr.setUpdating (true); Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gcol = Gtk::manage( new Inkscape::UI::Widget::RegisteredColorPicker( - _("Grid line _color:"), _("Grid line color"), _("Color of grid lines"), + _("Grid line _color:"), _("Grid line color"), _("Color of grid lines"), "color", "opacity", _wr, repr, doc)); Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gmcol = Gtk::manage( new Inkscape::UI::Widget::RegisteredColorPicker( - _("Ma_jor grid line color:"), _("Major grid line color"), + _("Ma_jor grid line color:"), _("Major grid line color"), _("Color of the major (highlighted) grid lines"), "empcolor", "empopacity", _wr, repr, doc)); @@ -656,7 +656,7 @@ CanvasAxonomGrid::Render (SPCanvasBuf *buf) } } -CanvasAxonomGridSnapper::CanvasAxonomGridSnapper(CanvasAxonomGrid *grid, SnapManager const *sm, Geom::Coord const d) : LineSnapper(sm, d) +CanvasAxonomGridSnapper::CanvasAxonomGridSnapper(CanvasAxonomGrid *grid, SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) { this->grid = grid; } @@ -685,64 +685,64 @@ CanvasAxonomGridSnapper::_getSnapLines(Geom::Point const &p) const // - 2 vertical grid lines, one left and one right from the point // - 2 angled z grid lines, one above and one below the point // - 2 angled x grid lines, one above and one below the point - + // Calculate the x coordinate of the vertical grid lines Geom::Coord x_max = Inkscape::Util::round_to_upper_multiple_plus(p[Geom::X], scaled_spacing_h, grid->origin[Geom::X]); Geom::Coord x_min = Inkscape::Util::round_to_lower_multiple_plus(p[Geom::X], scaled_spacing_h, grid->origin[Geom::X]); - + // Calculate the y coordinate of the intersection of the angled grid lines with the y-axis - double y_proj_along_z = p[Geom::Y] - grid->tan_angle[Z]*(p[Geom::X] - grid->origin[Geom::X]); - double y_proj_along_x = p[Geom::Y] + grid->tan_angle[X]*(p[Geom::X] - grid->origin[Geom::X]); + double y_proj_along_z = p[Geom::Y] - grid->tan_angle[Z]*(p[Geom::X] - grid->origin[Geom::X]); + double y_proj_along_x = p[Geom::Y] + grid->tan_angle[X]*(p[Geom::X] - grid->origin[Geom::X]); double y_proj_along_z_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_z, scaled_spacing_v, grid->origin[Geom::Y]); - double y_proj_along_z_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_z, scaled_spacing_v, grid->origin[Geom::Y]); + double y_proj_along_z_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_z, scaled_spacing_v, grid->origin[Geom::Y]); double y_proj_along_x_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_x, scaled_spacing_v, grid->origin[Geom::Y]); double y_proj_along_x_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_x, scaled_spacing_v, grid->origin[Geom::Y]); - + // Calculate the normal for the angled grid lines Geom::Point norm_x = Geom::rot90(Geom::Point(1, -grid->tan_angle[X])); Geom::Point norm_z = Geom::rot90(Geom::Point(1, grid->tan_angle[Z])); - + // The four angled grid lines form a parallellogram, enclosing the point // One of the two vertical grid lines divides this parallellogram in two triangles - // We will now try to find out in which half (i.e. triangle) our point is, and return - // only the three grid lines defining that triangle - - // The vertical grid line is at the intersection of two angled grid lines. + // We will now try to find out in which half (i.e. triangle) our point is, and return + // only the three grid lines defining that triangle + + // The vertical grid line is at the intersection of two angled grid lines. // Now go find that intersection! Geom::Point result; Geom::IntersectorKind is = Geom::line_intersection(norm_x, norm_x[Geom::Y]*y_proj_along_x_max, norm_z, norm_z[Geom::Y]*y_proj_along_z_max, result); - - // Determine which half of the parallellogram to use + + // Determine which half of the parallellogram to use bool use_left_half = true; bool use_right_half = true; - + if (is == Geom::intersects) { use_left_half = (p[Geom::X] - grid->origin[Geom::X]) < result[Geom::X]; - use_right_half = !use_left_half; + use_right_half = !use_left_half; } - + //std::cout << "intersection at " << result << " leads to use_left_half = " << use_left_half << " and use_right_half = " << use_right_half << std::endl; - + // Return the three grid lines which define the triangle that encloses our point // If we didn't find an intersection above, all 6 grid lines will be returned if (use_left_half) { s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_max))); s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_min))); - s.push_back(std::make_pair(component_vectors[Geom::X], Geom::Point(x_max, 0))); + s.push_back(std::make_pair(component_vectors[Geom::X], Geom::Point(x_max, 0))); } - + if (use_right_half) { s.push_back(std::make_pair(norm_z, Geom::Point(grid->origin[Geom::X], y_proj_along_z_min))); s.push_back(std::make_pair(norm_x, Geom::Point(grid->origin[Geom::X], y_proj_along_x_max))); - s.push_back(std::make_pair(component_vectors[Geom::X], Geom::Point(x_min, 0))); - } - + s.push_back(std::make_pair(component_vectors[Geom::X], Geom::Point(x_min, 0))); + } + return s; } -void CanvasAxonomGridSnapper::_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 +void CanvasAxonomGridSnapper::_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 { 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/display/canvas-axonomgrid.h b/src/display/canvas-axonomgrid.h index 1d255d798..0654a9429 100644 --- a/src/display/canvas-axonomgrid.h +++ b/src/display/canvas-axonomgrid.h @@ -36,7 +36,7 @@ public: void Update (Geom::Matrix const &affine, unsigned int flags); void Render (SPCanvasBuf *buf); - + void readRepr(); void onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive); @@ -44,15 +44,15 @@ public: double angle_deg[3]; /**< Angle of each axis (note that angle[2] == 0) */ double angle_rad[3]; /**< Angle of each axis (note that angle[2] == 0) */ double tan_angle[3]; /**< tan(angle[.]) */ - + bool scaled; /**< Whether the grid is in scaled mode */ - + Geom::Point ow; /**< Transformed origin by the affine for the zoom */ double lyw; /**< Transformed length y by the affine for the zoom */ double lxw_x; double lxw_z; double spacing_ylines; - + Geom::Point sw; /**< the scaling factors of the affine transform */ protected: @@ -61,7 +61,7 @@ protected: private: CanvasAxonomGrid(const CanvasAxonomGrid&); CanvasAxonomGrid& operator=(const CanvasAxonomGrid&); - + void updateWidgets(); }; @@ -70,20 +70,20 @@ private: class CanvasAxonomGridSnapper : public LineSnapper { public: - CanvasAxonomGridSnapper(CanvasAxonomGrid *grid, SnapManager const *sm, Geom::Coord const d); + CanvasAxonomGridSnapper(CanvasAxonomGrid *grid, SnapManager *sm, Geom::Coord const d); -private: +private: LineList _getSnapLines(Geom::Point const &p) const; void _addSnappedLine(SnappedConstraints &sc, Geom::Point const snapped_point, Geom::Coord const snapped_distance, Geom::Point const normal_to_line, const Geom::Point point_on_line) const; CanvasAxonomGrid *grid; -}; +}; }; //namespace Inkscape -#endif +#endif diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index 8505426d3..b03bfca9e 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -14,7 +14,7 @@ #define INKSCAPE_CANVAS_GRID_C #include "sp-canvas-util.h" -#include "util/mathfns.h" +#include "util/mathfns.h" #include "display-forward.h" #include #include "desktop-handles.h" @@ -357,13 +357,13 @@ CanvasGrid::on_repr_attr_changed(Inkscape::XML::Node *repr, gchar const *key, gc ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive); } -bool CanvasGrid::isEnabled() -{ +bool CanvasGrid::isEnabled() +{ if (snapper == NULL) { return false; - } - - return snapper->getEnabled(); + } + + return snapper->getEnabled(); } // ########################################################## @@ -619,7 +619,7 @@ CanvasXYGrid::readRepr() if ( (value = repr->attribute("visible")) ) { visible = (strcmp(value,"false") != 0 && strcmp(value, "0") != 0); } - + if ( (value = repr->attribute("enabled")) ) { g_assert(snapper != NULL); snapper->setEnabled(strcmp(value,"false") != 0 && strcmp(value, "0") != 0); @@ -665,15 +665,15 @@ CanvasXYGrid::newSpecificWidget() Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gcol = Gtk::manage( new Inkscape::UI::Widget::RegisteredColorPicker( - _("Grid line _color:"), _("Grid line color"), _("Color of grid lines"), + _("Grid line _color:"), _("Grid line color"), _("Color of grid lines"), "color", "opacity", _wr, repr, doc)); Inkscape::UI::Widget::RegisteredColorPicker *_rcp_gmcol = Gtk::manage( new Inkscape::UI::Widget::RegisteredColorPicker( - _("Ma_jor grid line color:"), _("Major grid line color"), - _("Color of the major (highlighted) grid lines"), "empcolor", "empopacity", + _("Ma_jor grid line color:"), _("Major grid line color"), + _("Color of the major (highlighted) grid lines"), "empcolor", "empopacity", _wr, repr, doc)); - + Inkscape::UI::Widget::RegisteredSuffixedInteger *_rsi = Gtk::manage( new Inkscape::UI::Widget::RegisteredSuffixedInteger( _("_Major grid line every:"), "", _("lines"), "empspacing", _wr, repr, doc) ); @@ -948,7 +948,7 @@ CanvasXYGrid::Render (SPCanvasBuf *buf) } } -CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SnapManager const *sm, Geom::Coord const d) : LineSnapper(sm, d) +CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) { this->grid = grid; } @@ -973,13 +973,13 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const scaled_spacing /= SP_ACTIVE_DESKTOP->current_zoom(); } - Geom::Coord rounded; + Geom::Coord rounded; Geom::Point point_on_line; - + rounded = Inkscape::Util::round_to_upper_multiple_plus(p[i], scaled_spacing, grid->origin[i]); point_on_line = i ? Geom::Point(0, rounded) : Geom::Point(rounded, 0); s.push_back(std::make_pair(component_vectors[i], point_on_line)); - + rounded = Inkscape::Util::round_to_lower_multiple_plus(p[i], scaled_spacing, grid->origin[i]); point_on_line = i ? Geom::Point(0, rounded) : Geom::Point(rounded, 0); s.push_back(std::make_pair(component_vectors[i], point_on_line)); @@ -988,7 +988,7 @@ CanvasXYGridSnapper::_getSnapLines(Geom::Point const &p) const return s; } -void CanvasXYGridSnapper::_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 +void CanvasXYGridSnapper::_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 { 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/display/canvas-grid.h b/src/display/canvas-grid.h index f50537065..e4bd01414 100644 --- a/src/display/canvas-grid.h +++ b/src/display/canvas-grid.h @@ -90,9 +90,9 @@ public: guint32 color; /**< Color for normal lines */ guint32 empcolor; /**< Color for emphasis lines */ gint empspacing; /**< Spacing between emphasis lines */ - + SPUnit const* gridunit; - + Inkscape::XML::Node * repr; SPDocument *doc; @@ -158,7 +158,7 @@ private: class CanvasXYGridSnapper : public LineSnapper { public: - CanvasXYGridSnapper(CanvasXYGrid *grid, SnapManager const *sm, Geom::Coord const d); + CanvasXYGridSnapper(CanvasXYGrid *grid, SnapManager *sm, Geom::Coord const d); bool ThisSnapperMightSnap() const; private: diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index d9c57f829..be2e84170 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -17,7 +17,7 @@ #include "sp-namedview.h" #include "sp-guide.h" -Inkscape::GuideSnapper::GuideSnapper(SnapManager const *sm, Geom::Coord const d) : LineSnapper(sm, d) +Inkscape::GuideSnapper::GuideSnapper(SnapManager *sm, Geom::Coord const d) : LineSnapper(sm, d) { } @@ -32,7 +32,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); - 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)); } return s; diff --git a/src/guide-snapper.h b/src/guide-snapper.h index a7798c72f..f36d03499 100644 --- a/src/guide-snapper.h +++ b/src/guide-snapper.h @@ -10,7 +10,7 @@ * Frank Felfe * Carl Hetherington * - * Copyright (C) 1999-2002 Authors + * Copyright (C) 1999-2002 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -28,11 +28,11 @@ namespace Inkscape class GuideSnapper : public LineSnapper { public: - GuideSnapper(SnapManager const *sm, Geom::Coord const d); + GuideSnapper(SnapManager *sm, Geom::Coord const d); bool ThisSnapperMightSnap() const; private: - LineList _getSnapLines(Geom::Point const &p) const; + LineList _getSnapLines(Geom::Point const &p) const; void _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 f2d2f2ff6..bfcda3f79 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -5,7 +5,7 @@ * Authors: * Diederik van Lierop * And others... - * + * * Copyright (C) 1999-2008 Authors * * Released under GNU GPL, read the file 'COPYING' for more information @@ -19,7 +19,7 @@ #include #include "snap.h" -Inkscape::LineSnapper::LineSnapper(SnapManager const *sm, Geom::Coord const d) : Snapper(sm, d) +Inkscape::LineSnapper::LineSnapper(SnapManager *sm, Geom::Coord const d) : Snapper(sm, d) { } @@ -33,8 +33,8 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, { if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false) { return; - } - + } + /* Get the lines that we will try to snap to */ const LineList lines = _getSnapLines(p); @@ -45,16 +45,16 @@ void Inkscape::LineSnapper::freeSnap(SnappedConstraints &sc, Geom::Point const p2 = p1 + Geom::rot90(i->first); // 2nd point at guide/grid line // std::cout << " line through " << i->second << " with normal " << i->first; g_assert(i->first != Geom::Point(0,0)); // we cannot project on an linesegment of zero length - + Geom::Point const p_proj = project_on_linesegment(p, p1, p2); Geom::Coord const dist = Geom::L2(p_proj - p); //Store any line that's within snapping range if (dist < getSnapperTolerance()) { _addSnappedLine(sc, p_proj, dist, i->first, i->second); - // std::cout << " -> distance = " << dist; - } + // std::cout << " -> distance = " << dist; + } // std::cout << std::endl; - } + } } void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, @@ -69,7 +69,7 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false) { return; } - + /* Get the lines that we will try to snap to */ const LineList lines = _getSnapLines(p); @@ -77,19 +77,19 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, if (Geom::L2(c.getDirection()) > 0) { // Can't do a constrained snap without a constraint /* Normal to the line we're trying to snap along */ Geom::Point const n(Geom::rot90(Geom::unit_vector(c.getDirection()))); - + Geom::Point const point_on_line = c.hasPoint() ? c.getPoint() : p; - + /* Constant term of the line we're trying to snap along */ Geom::Coord const q0 = dot(n, point_on_line); /* Constant term of the grid or guide line */ - Geom::Coord const q1 = dot(i->first, i->second); - + Geom::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, q0, i->first, q1, t_2geom); Geom::Point t(t_2geom); - + if (k == Geom::intersects) { const Geom::Coord dist = L2(t - p); if (dist < getSnapperTolerance()) { @@ -98,8 +98,8 @@ void Inkscape::LineSnapper::constrainedSnap(SnappedConstraints &sc, // to look for additional intersections; just return the snapped point // and forget about the line sc.points.push_back(SnappedPoint(t, Inkscape::SNAPTARGET_UNDEFINED, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true)); - // The type of the snap target is yet undefined, as we cannot tell whether - // we're snapping to grid or the guide lines; must be set by on a higher level + // The type of the snap target is yet undefined, as we cannot tell whether + // we're snapping to grid or the guide lines; must be set by on a higher level } } } diff --git a/src/line-snapper.h b/src/line-snapper.h index 529db1caa..91e8ca596 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -8,7 +8,7 @@ * Authors: * Carl Hetherington * Diederik van Lierop - * + * * Copyright (C) 1999-2008 Authors * * Released under GNU GPL, read the file 'COPYING' for more information. @@ -22,7 +22,7 @@ namespace Inkscape class LineSnapper : public Snapper { public: - LineSnapper(SnapManager const *sm, Geom::Coord const d); + LineSnapper(SnapManager *sm, Geom::Coord const d); void freeSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, @@ -31,7 +31,7 @@ public: Geom::OptRect const &bbox_to_snap, std::vector const *it, std::vector *unselected_nodes) const; - + void constrainedSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, @@ -41,7 +41,7 @@ public: std::vector const *it) const; protected: - typedef std::list > LineList; + typedef std::list > LineList; //first point is a vector normal to the line //second point is a point on the line @@ -51,7 +51,7 @@ private: * \return List of lines that we should try snapping to. */ virtual LineList _getSnapLines(Geom::Point const &p) const = 0; - + virtual void _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 = 0; }; diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 06f24c47f..922c35bcb 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -36,14 +36,14 @@ Inkscape::SnapCandidate::SnapCandidate(SPItem* item, bool clip_or_mask, Geom::Matrix additional_affine) : item(item), clip_or_mask(clip_or_mask), additional_affine(additional_affine) -{ +{ } Inkscape::SnapCandidate::~SnapCandidate() -{ +{ } -Inkscape::ObjectSnapper::ObjectSnapper(SnapManager const *sm, Geom::Coord const d) +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) @@ -84,18 +84,18 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, { bool const c1 = (snap_dim == TRANSL_SNAP_XY) && ThisSnapperMightSnap(); bool const c2 = (snap_dim != TRANSL_SNAP_XY) && GuidesMightSnap(); - + if (!(c1 || c2)) { - return; + return; } - + if (first_point) { _candidates->clear(); } - + Geom::Rect bbox_to_snap_incl = bbox_to_snap; // _incl means: will include the snapper tolerance bbox_to_snap_incl.expandBy(getSnapperTolerance()); // see? - + for (SPObject* o = sp_object_first_child(parent); o != NULL; o = SP_OBJECT_NEXT(o)) { g_assert(_snapmanager->getDesktop() != NULL); if (SP_IS_ITEM(o) && !SP_ITEM(o)->isLocked() && !(_snapmanager->getDesktop()->itemIsHidden(SP_ITEM(o)) && !clip_or_mask)) { @@ -109,32 +109,32 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, i++; } } - + if (it == NULL || i == it->end()) { - SPItem *item = SP_ITEM(o); + SPItem *item = SP_ITEM(o); Geom::Matrix transform = Geom::identity(); if (item) { SPObject *obj = NULL; if (clip_or_mask) { // If the current item is a clipping path or a mask // then store the transformation of the clipped path or mask itself - // but also take into account the additional affine of the object + // but also take into account the additional affine of the object // being clipped / masked transform = to_2geom(item->transform) * additional_affine; - } else { // cannot clip or mask more than once + } else { // cannot clip or mask more than once // The current item is not a clipping path or a mask, but might // still be the subject of clipping or masking itself ; if so, then // we should also consider that path or mask for snapping to obj = SP_OBJECT(item->clip_ref->getObject()); if (obj) { _findCandidates(obj, it, false, bbox_to_snap, snap_dim, true, item->transform); - } + } obj = SP_OBJECT(item->mask_ref->getObject()); if (obj) { _findCandidates(obj, it, false, bbox_to_snap, snap_dim, true, item->transform); } } - } - + } + if (SP_IS_GROUP(o)) { _findCandidates(o, it, false, bbox_to_snap, snap_dim, false, Geom::identity()); } else { @@ -142,7 +142,7 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, if (clip_or_mask) { // Oh oh, this will get ugly. We cannot use sp_item_i2d_affine directly because we need to // insert an additional transformation in document coordinates (code copied from sp_item_i2d_affine) - sp_item_invoke_bbox(item, + sp_item_invoke_bbox(item, bbox_of_item, sp_item_i2doc_affine(item) * matrix_to_desktop(additional_affine, item), true); @@ -150,11 +150,11 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* parent, sp_item_invoke_bbox(item, bbox_of_item, sp_item_i2d_affine(item), true); } if (bbox_of_item) { - // See if the item is within range + // See if the item is within range if (bbox_to_snap_incl.intersects(*bbox_of_item)) { // This item is within snapping range, so record it as a candidate _candidates->push_back(SnapCandidate(item, clip_or_mask, additional_affine)); - } + } } } } @@ -171,27 +171,27 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType // first point and store the collection for later use. This significantly improves the performance if (first_point) { _points_to_snap_to->clear(); - + // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; - + bool p_is_a_node = t & Inkscape::SnapPreferences::SNAPPOINT_NODE; bool p_is_a_bbox = t & Inkscape::SnapPreferences::SNAPPOINT_BBOX; bool p_is_a_guide = t & Inkscape::SnapPreferences::SNAPPOINT_GUIDE; - + // 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))); - + 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) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool prefs_bbox = prefs->getBool("/tools/bounding_box"); - bbox_type = !prefs_bbox ? + bbox_type = !prefs_bbox ? SPItem::APPROXIMATE_BBOX : SPItem::GEOMETRIC_BBOX; } - + // Consider the page border for snapping if (_snap_to_page_border) { - _getBorderNodes(_points_to_snap_to); + _getBorderNodes(_points_to_snap_to); } for (std::vector::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) { @@ -205,7 +205,35 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType //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); + } } } @@ -214,7 +242,7 @@ void Inkscape::ObjectSnapper::_collectNodes(Inkscape::SnapPreferences::PointType 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) { + if (!(*i).clip_or_mask) { Geom::OptRect b = sp_item_bbox_desktop(root_item, bbox_type); if (b) { for ( unsigned k = 0 ; k < 4 ; k++ ) { @@ -235,18 +263,18 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, std::vector *unselected_nodes) const { // Iterate through all nodes, find out which one is the closest to p, and snap to it! - + _collectNodes(t, first_point); - + if (unselected_nodes != NULL) { _points_to_snap_to->insert(_points_to_snap_to->end(), unselected_nodes->begin(), unselected_nodes->end()); - } - + } + SnappedPoint s; bool success = false; - + for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { - Geom::Coord dist = Geom::L2(*k - p); + Geom::Coord dist = Geom::L2(*k - p); if (dist < getSnapperTolerance() && dist < s.getSnapDistance()) { s = SnappedPoint(*k, SNAPTARGET_NODE, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); success = true; @@ -254,7 +282,7 @@ void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, } if (success) { - sc.points.push_back(s); + sc.points.push_back(s); } } @@ -265,16 +293,16 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s { // Iterate through all nodes, find out which one is the closest to this guide, and snap to it! _collectNodes(t, true); - + SnappedPoint s; bool success = false; - + Geom::Coord tol = getSnapperTolerance(); - + for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { // Project each node (*k) on the guide line (running through point p) Geom::Point p_proj = project_on_linesegment(*k, p, p + Geom::rot90(guide_normal)); - Geom::Coord dist = Geom::L2(*k - p_proj); // distance from node to the guide + Geom::Coord dist = Geom::L2(*k - p_proj); // distance from node to the guide Geom::Coord dist2 = Geom::L2(p - p_proj); // distance from projection of node on the guide, to the mouse location if ((dist < tol && dist2 < tol) || (getSnapperAlwaysSnap() && dist < s.getSnapDistance())) { s = SnappedPoint(*k, SNAPTARGET_NODE, dist, tol, getSnapperAlwaysSnap(), true); @@ -283,7 +311,7 @@ void Inkscape::ObjectSnapper::_snapTranslatingGuideToNodes(SnappedConstraints &s } if (success) { - sc.points.push_back(s); + sc.points.push_back(s); } } @@ -300,19 +328,19 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType // first point and store the collection for later use. This significantly improves the performance if (first_point) { _clear_paths(); - + // Determine the type of bounding box we should snap to SPItem::BBoxType bbox_type = SPItem::GEOMETRIC_BBOX; - + bool p_is_a_node = t & Inkscape::SnapPreferences::SNAPPOINT_NODE; - + if (_snap_to_bboxpath) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); int prefs_bbox = prefs->getBool("/tools/bounding_box", 0); - bbox_type = !prefs_bbox ? + bbox_type = !prefs_bbox ? SPItem::APPROXIMATE_BBOX : SPItem::GEOMETRIC_BBOX; } - + // Consider the page border for snapping if (_snap_to_page_border) { Geom::PathVector *border_path = _getBorderPathv(); @@ -320,7 +348,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType _paths_to_snap_to->push_back(border_path); } } - + for (std::vector::const_iterator i = _candidates->begin(); i != _candidates->end(); i++) { /* Transform the requested snap point to this item's coordinates */ @@ -335,7 +363,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType i2doc = sp_item_i2doc_affine((*i).item); root_item = (*i).item; } - + //Build a list of all paths considered for snapping to //Add the item's path to snap to @@ -361,10 +389,10 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType } if (!very_lenghty_prose && !very_complex_path) { - SPCurve *curve = curve_for_item(root_item); + SPCurve *curve = curve_for_item(root_item); if (curve) { // We will get our own copy of the path, which must be freed at some point - Geom::PathVector *borderpathv = pathvector_for_curve(root_item, curve, true, true, Geom::identity(), (*i).additional_affine); + 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. curve->unref(); } @@ -377,12 +405,12 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType if (!(_strict_snapping && 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) { - Geom::OptRect rect; - sp_item_invoke_bbox(root_item, rect, i2doc, TRUE, bbox_type); + if (!(*i).clip_or_mask) { + Geom::OptRect rect; + sp_item_invoke_bbox(root_item, rect, i2doc, TRUE, bbox_type); if (rect) { - Geom::PathVector *path = _getPathvFromRect(*rect); - _paths_to_snap_to->push_back(path); + Geom::PathVector *path = _getPathvFromRect(*rect); + _paths_to_snap_to->push_back(path); } } } @@ -390,7 +418,7 @@ void Inkscape::ObjectSnapper::_collectPaths(Inkscape::SnapPreferences::PointType } } } - + void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, @@ -400,23 +428,23 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, { _collectPaths(t, first_point); // Now we can finally do the real snapping, using the paths collected above - - g_assert(_snapmanager->getDesktop() != NULL); + + 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; - + if (first_point) { /* findCandidates() is used for snapping to both paths and nodes. It ignores the path that is - * currently being edited, because that path requires special care: when snapping to nodes + * currently being edited, because that path requires special care: when snapping to nodes * only the unselected nodes of that path should be considered, and these will be passed on separately. * This path must not be ignored however when snapping to the paths, so we add it here - * manually when applicable. - * + * manually when applicable. + * * Note that this path must be the last in line! - * */ - if (node_tool_active) { - SPCurve *curve = curve_for_item(SP_ITEM(selected_path)); + * */ + if (node_tool_active) { + SPCurve *curve = curve_for_item(SP_ITEM(selected_path)); 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 _paths_to_snap_to->push_back(pathv); @@ -424,77 +452,77 @@ void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, } } } - + for (std::vector::const_iterator it_p = _paths_to_snap_to->begin(); it_p != _paths_to_snap_to->end(); it_p++) { - bool const being_edited = (node_tool_active && (*it_p) == _paths_to_snap_to->back()); + bool const being_edited = (node_tool_active && (*it_p) == _paths_to_snap_to->back()); //if true then this pathvector it_pv is currently being edited in the node tool - + // char * svgd = sp_svg_write_path(**it_p); - // std::cout << "Dumping the pathvector: " << svgd << std::endl; - + // std::cout << "Dumping the pathvector: " << svgd << std::endl; + for(Geom::PathVector::iterator it_pv = (*it_p)->begin(); it_pv != (*it_p)->end(); ++it_pv) { // Find a nearest point for each curve within this path // n curves will return n time values with 0 <= t <= 1 std::vector anp = (*it_pv).nearestPointPerCurve(p_doc); - + std::vector::const_iterator np = anp.begin(); unsigned int index = 0; for (; np != anp.end(); np++, index++) { Geom::Curve const *curve = &((*it_pv).at_index(index)); Geom::Point const sp_doc = curve->pointAt(*np); - + bool c1 = true; - bool c2 = true; + bool c2 = true; if (being_edited) { /* If the path is being edited, then we should only snap though to stationary pieces of the path - * and not to the pieces that are being dragged around. This way we avoid + * and not to the pieces that are being dragged around. This way we avoid * self-snapping. For this we check whether the nodes at both ends of the current - * piece are unselected; if they are then this piece must be stationary - */ + * piece are unselected; if they are then this piece must be stationary + */ g_assert(unselected_nodes != NULL); - Geom::Point start_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(0)); - Geom::Point end_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(1)); - c1 = isUnselectedNode(start_pt, unselected_nodes); - c2 = isUnselectedNode(end_pt, unselected_nodes); + Geom::Point start_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(0)); + Geom::Point end_pt = _snapmanager->getDesktop()->doc2dt(curve->pointAt(1)); + c1 = isUnselectedNode(start_pt, unselected_nodes); + c2 = isUnselectedNode(end_pt, unselected_nodes); /* Unfortunately, this might yield false positives for coincident nodes. Inkscape might therefore mistakenly * snap to path segments that are not stationary. There are at least two possible ways to overcome this: * - Linking the individual nodes of the SPPath we have here, to the nodes of the NodePath::SubPath class as being * used in sp_nodepath_selected_nodes_move. This class has a member variable called "selected". For this the nodes * should be in the exact same order for both classes, so we can index them * - Replacing the SPPath being used here by the the NodePath::SubPath class; but how? - */ + */ } - - Geom::Point const sp_dt = _snapmanager->getDesktop()->doc2dt(sp_doc); + + Geom::Point const sp_dt = _snapmanager->getDesktop()->doc2dt(sp_doc); if (!being_edited || (c1 && c2)) { Geom::Coord const dist = Geom::distance(sp_doc, p_doc); if (dist < getSnapperTolerance()) { - sc.curves.push_back(Inkscape::SnappedCurve(from_2geom(sp_dt), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve)); + sc.curves.push_back(Inkscape::SnappedCurve(from_2geom(sp_dt), dist, getSnapperTolerance(), getSnapperAlwaysSnap(), false, curve)); } } - } - } // End of: for (Geom::PathVector::iterator ....) - } + } + } // End of: for (Geom::PathVector::iterator ....) + } } -/* Returns true if point is coincident with one of the unselected nodes */ +/* Returns true if point is coincident with one of the unselected nodes */ bool Inkscape::ObjectSnapper::isUnselectedNode(Geom::Point const &point, std::vector const *unselected_nodes) const { if (unselected_nodes == NULL) { return false; } - + if (unselected_nodes->size() == 0) { return false; } - + for (std::vector::const_iterator i = unselected_nodes->begin(); i != unselected_nodes->end(); i++) { if (Geom::L2(point - *i) < 1e-4) { return true; - } - } - - return false; + } + } + + return false; } void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, @@ -503,52 +531,52 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(SnappedConstraints &sc, bool const &first_point, ConstraintLine const &c) const { - + _collectPaths(t, first_point); - + // Now we can finally do the real snapping, using the paths collected above - + g_assert(_snapmanager->getDesktop() != NULL); - Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p); - + Geom::Point const p_doc = _snapmanager->getDesktop()->dt2doc(p); + Geom::Point direction_vector = c.getDirection(); if (!is_zero(direction_vector)) { direction_vector = Geom::unit_vector(direction_vector); } - + Geom::Point const p1_on_cl = c.hasPoint() ? c.getPoint() : p; Geom::Point const p2_on_cl = p1_on_cl + direction_vector; - - // The intersection point of the constraint line with any path, + + // 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 snapping tolerance Geom::Point const p_proj_on_cl = project_on_linesegment(p, p1_on_cl, p2_on_cl); - Geom::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector); + Geom::Point const p_min_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl - getSnapperTolerance() * direction_vector); Geom::Point const p_max_on_cl = _snapmanager->getDesktop()->dt2doc(p_proj_on_cl + getSnapperTolerance() * direction_vector); - + Geom::Path cl; - std::vector clv; + std::vector clv; cl.start(p_min_on_cl); cl.appendNew(p_max_on_cl); clv.push_back(cl); - + for (std::vector::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { - if (*k) { + if (*k) { Geom::CrossingSet cs = Geom::crossings(clv, *(*k)); if (cs.size() > 0) { // We need only the first element of cs, because cl is only a single straight linesegment // This first element contains a vector filled with crossings of cl with *k - for (std::vector::const_iterator m = cs[0].begin(); m != cs[0].end(); m++) { + for (std::vector::const_iterator m = cs[0].begin(); m != cs[0].end(); m++) { if ((*m).ta >= 0 && (*m).ta <= 1 ) { // Reconstruct the point of intersection Geom::Point p_inters = p_min_on_cl + ((*m).ta) * (p_max_on_cl - p_min_on_cl); // When it's within snapping range, then return it - // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1) + // (within snapping range == between p_min_on_cl and p_max_on_cl == 0 < ta < 1) Geom::Coord dist = Geom::L2(_snapmanager->getDesktop()->dt2doc(p_proj_on_cl) - p_inters); SnappedPoint s(_snapmanager->getDesktop()->doc2dt(p_inters), SNAPTARGET_PATH, dist, getSnapperTolerance(), getSnapperAlwaysSnap(), true); sc.points.push_back(s); - } - } + } + } } } } @@ -563,7 +591,7 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, std::vector const *it, std::vector *unselected_nodes) const { - if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false ) { + if (_snap_enabled == false || _snapmanager->snapprefs.getSnapFrom(t) == false ) { return; } @@ -572,29 +600,29 @@ void Inkscape::ObjectSnapper::freeSnap(SnappedConstraints &sc, Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p, p); _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) { _snapNodes(sc, t, p, first_point, unselected_nodes); } - + if (_snap_to_itempath || _snap_to_bboxpath || _snap_to_page_border) { 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 + /* While editing a path in the node tool, findCandidates must ignore that path because * of the node snapping requirements (i.e. only unselected nodes must be snapable). * That path must not be ignored however when snapping to the paths, so we add it here * manually when applicable - */ + */ SPPath *path = NULL; if (it != NULL) { g_assert(SP_IS_PATH(*it->begin())); g_assert(it->size() == 1); path = SP_PATH(*it->begin()); } - _snapPaths(sc, t, p, first_point, unselected_nodes, path); + _snapPaths(sc, t, p, first_point, unselected_nodes, path); } else { - _snapPaths(sc, t, p, first_point, NULL, NULL); - } + _snapPaths(sc, t, p, first_point, NULL, NULL); + } } } @@ -615,15 +643,15 @@ void Inkscape::ObjectSnapper::constrainedSnap( SnappedConstraints &sc, Geom::Rect const local_bbox_to_snap = bbox_to_snap ? *bbox_to_snap : Geom::Rect(p, p); _findCandidates(sp_document_root(_snapmanager->getDocument()), it, first_point, local_bbox_to_snap, TRANSL_SNAP_XY, false, Geom::identity()); } - + // A constrained snap, is a snap in only one degree of freedom (specified by the constraint line). // This is usefull for example when scaling an object while maintaining a fixed aspect ratio. It's // nodes are only allowed to move in one direction (i.e. in one degree of freedom). - + // When snapping to objects, we either snap to their nodes or their paths. It is however very // unlikely that any node will be exactly at the constrained line, so for a constrained snap // 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. + // so we will more or less snap to them anyhow. if (_snap_to_itempath || _snap_to_bboxpath || _snap_to_page_border) { _snapPathsConstrained(sc, t, p, first_point, c); @@ -639,7 +667,7 @@ void Inkscape::ObjectSnapper::guideSnap(SnappedConstraints &sc, /* Get a list of all the SPItems that we will try to snap to */ std::vector cand; std::vector const it; //just an empty list - + DimensionToSnap snap_dim; if (guide_normal == to_2geom(component_vectors[Geom::Y])) { snap_dim = GUIDE_TRANSL_SNAP_Y; @@ -648,20 +676,20 @@ void Inkscape::ObjectSnapper::guideSnap(SnappedConstraints &sc, } else { snap_dim = ANGLED_GUIDE_TRANSL_SNAP; } - - // We don't support ANGLED_GUIDE_ROT_SNAP yet. - + + // We don't support ANGLED_GUIDE_ROT_SNAP yet. + // It would be cool to allow the user to rotate a guide by dragging it, instead of - // only translating it. (For example when CTRL is pressed). We will need an UI part - // for that first; and some important usability choices need to be made: + // only translating it. (For example when CTRL is pressed). We will need an UI part + // for that first; and some important usability choices need to be made: // E.g. which point should be used for pivoting? A previously snapped point, // or a transformation center (which can be moved after clicking for the // second time on an object; but should this point then be constrained to the // line, or can it be located anywhere?) - + _findCandidates(sp_document_root(_snapmanager->getDocument()), &it, true, Geom::Rect(p, p), snap_dim, false, Geom::identity()); _snapTranslatingGuideToNodes(sc, Inkscape::SnapPreferences::SNAPPOINT_GUIDE, p, guide_normal); - // _snapRotatingGuideToNodes has not been implemented yet. + // _snapRotatingGuideToNodes has not been implemented yet. } /** @@ -679,7 +707,7 @@ bool Inkscape::ObjectSnapper::GuidesMightSnap() const return (_snap_enabled && _snapmanager->snapprefs.getSnapModeGuide() && snap_to_something); } -void Inkscape::ObjectSnapper::_clear_paths() const +void Inkscape::ObjectSnapper::_clear_paths() const { for (std::vector::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { g_free(*k); @@ -690,7 +718,7 @@ void Inkscape::ObjectSnapper::_clear_paths() const Geom::PathVector* Inkscape::ObjectSnapper::_getBorderPathv() const { Geom::Rect const border_rect = Geom::Rect(Geom::Point(0,0), Geom::Point(sp_document_width(_snapmanager->getDocument()),sp_document_height(_snapmanager->getDocument()))); - return _getPathvFromRect(border_rect); + return _getPathvFromRect(border_rect); } Geom::PathVector* Inkscape::ObjectSnapper::_getPathvFromRect(Geom::Rect const rect) const @@ -701,7 +729,7 @@ Geom::PathVector* Inkscape::ObjectSnapper::_getPathvFromRect(Geom::Rect const re return dummy; } else { return NULL; - } + } } void Inkscape::ObjectSnapper::_getBorderNodes(std::vector *points) const diff --git a/src/object-snapper.h b/src/object-snapper.h index f235ae566..9972677e0 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -9,7 +9,7 @@ * Carl Hetherington * Diederik van Lierop * - * Copyright (C) 2005 - 2008 Authors + * Copyright (C) 2005 - 2008 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */ @@ -26,31 +26,31 @@ namespace Inkscape { class SnapCandidate - + { public: SnapCandidate(SPItem* item, bool clip_or_mask, Geom::Matrix _additional_affine); ~SnapCandidate(); - + SPItem* item; // An item that is to be considered for snapping to bool clip_or_mask; // If true, then item refers to a clipping path or a mask - - /* To find out the absolute position of a clipping path or mask, we not only need to know - * the transformation of the clipping path or mask itself, but also the transformation of + + /* To find out the absolute position of a clipping path or mask, we not only need to know + * the transformation of the clipping path or mask itself, but also the transformation of * the object to which the clip or mask is being applied; that transformation is stored here */ - Geom::Matrix additional_affine; + Geom::Matrix additional_affine; }; class ObjectSnapper : public Snapper { public: - ObjectSnapper(SnapManager const *sm, Geom::Coord const d); + ObjectSnapper(SnapManager *sm, Geom::Coord const d); ~ObjectSnapper(); enum DimensionToSnap { - GUIDE_TRANSL_SNAP_X, // For snapping a vertical guide (normal in the X-direction) to objects, + GUIDE_TRANSL_SNAP_X, // For snapping a vertical guide (normal in the X-direction) to objects, GUIDE_TRANSL_SNAP_Y, // For snapping a horizontal guide (normal in the Y-direction) to objects ANGLED_GUIDE_TRANSL_SNAP, // For snapping an angled guide, while translating it accross the desktop ANGLED_GUIDE_ROT_SNAP, // For snapping an angled guide, while rotating it around some pivot point @@ -69,10 +69,10 @@ public: void guideSnap(SnappedConstraints &sc, Geom::Point const &p, Geom::Point const &guide_normal) const; - + bool ThisSnapperMightSnap() const; bool GuidesMightSnap() const; - + void freeSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, @@ -84,17 +84,17 @@ public: void constrainedSnap(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, - bool const &first_point, + bool const &first_point, Geom::OptRect const &bbox_to_snap, ConstraintLine const &c, std::vector const *it) const; - + private: //store some lists of candidates, points and paths, so we don't have to rebuild them for each point we want to snap - std::vector *_candidates; + std::vector *_candidates; std::vector *_points_to_snap_to; std::vector *_paths_to_snap_to; - + void _findCandidates(SPObject* parent, std::vector const *it, bool const &first_point, @@ -102,55 +102,55 @@ private: DimensionToSnap snap_dim, bool const _clip_or_mask, Geom::Matrix const additional_affine) const; - + void _snapNodes(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, // in desktop coordinates bool const &first_point, std::vector *unselected_nodes) const; // in desktop coordinates - + void _snapTranslatingGuideToNodes(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, Geom::Point const &guide_normal) const; - + void _collectNodes(Inkscape::SnapPreferences::PointType const &t, bool const &first_point) const; - + void _snapPaths(SnappedConstraints &sc, - Inkscape::SnapPreferences::PointType const &t, + Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, // in desktop coordinates bool const &first_point, std::vector *unselected_nodes, // in desktop coordinates SPPath const *selected_path) const; - + void _snapPathsConstrained(SnappedConstraints &sc, Inkscape::SnapPreferences::PointType const &t, Geom::Point const &p, // in desktop coordinates bool const &first_point, ConstraintLine const &c) const; - + bool isUnselectedNode(Geom::Point const &point, std::vector const *unselected_nodes) const; - - void _collectPaths(Inkscape::SnapPreferences::PointType const &t, + + void _collectPaths(Inkscape::SnapPreferences::PointType const &t, bool const &first_point) const; - + void _clear_paths() const; Geom::PathVector* _getBorderPathv() const; 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, + + //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; + bool _strict_snapping; }; } diff --git a/src/snap-preferences.h b/src/snap-preferences.h index d98cd7cef..a52e0cfd9 100644 --- a/src/snap-preferences.h +++ b/src/snap-preferences.h @@ -20,7 +20,7 @@ class SnapPreferences { public: SnapPreferences(); - + /// Point types to snap. typedef int PointType; static const PointType SNAPPOINT_NODE; @@ -39,31 +39,34 @@ 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;} bool getSnapIntersectionGG() const {return _intersectionGG;} - bool getSnapIntersectionCS() const {return _intersectionCS;} + bool getSnapIntersectionCS() const {return _intersectionCS;} bool getSnapSmoothNodes() const {return _smoothNodes;} - + bool getSnapMidpoints() const {return _midpoints;} + void setIncludeItemCenter(bool enabled) {_include_item_center = enabled;} bool getIncludeItemCenter() const {return _include_item_center;} - - void setSnapEnabledGlobally(bool enabled) {_snap_enabled_globally = enabled;} - bool getSnapEnabledGlobally() const {return _snap_enabled_globally;} - + + void setSnapEnabledGlobally(bool enabled) {_snap_enabled_globally = enabled;} + bool getSnapEnabledGlobally() const {return _snap_enabled_globally;} + void setSnapPostponedGlobally(bool postponed) {_snap_postponed_globally = postponed;} bool getSnapPostponedGlobally() const {return _snap_postponed_globally;} - + void setSnapFrom(PointType t, bool s); bool getSnapFrom(PointType t) const; - + private: bool _include_item_center; //If true, snapping nodes will also snap the item's center - bool _intersectionGG; - bool _intersectionCS; + bool _intersectionGG; //Consider snapping to intersections of grid and guides + bool _intersectionCS; //Consider snapping to intersections of curves bool _smoothNodes; + bool _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 - + }; } diff --git a/src/snap.h b/src/snap.h index b8d9fdd25..14adf399f 100644 --- a/src/snap.h +++ b/src/snap.h @@ -48,44 +48,44 @@ public: STRETCH, SKEW }; - + SnapManager(SPNamedView const *v); typedef std::list SnapperList; bool someSnapperMightSnap() 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); - // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a - // point if snapping has occured (by overwriting p); otherwise p is untouched + // freeSnapReturnByRef() is preferred over freeSnap(), because it only returns a + // point if snapping has occured (by overwriting p); otherwise p is untouched void freeSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, Geom::Point &p, bool first_point = true, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; - + Inkscape::SnappedPoint freeSnap(Inkscape::SnapPreferences::PointType point_type, Geom::Point const &p, bool first_point = true, Geom::OptRect const &bbox_to_snap = Geom::OptRect() ) const; - + Geom::Point multipleOfGridPitch(Geom::Point const &t) const; - - // constrainedSnapReturnByRef() is preferred over constrainedSnap(), because it only returns a + + // constrainedSnapReturnByRef() is preferred over constrainedSnap(), because it only returns a // point, by overwriting p, if snapping has occured; otherwise p is untouched void constrainedSnapReturnByRef(Inkscape::SnapPreferences::PointType point_type, Geom::Point &p, Inkscape::Snapper::ConstraintLine const &constraint, bool first_point = true, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; - + Inkscape::SnappedPoint constrainedSnap(Inkscape::SnapPreferences::PointType point_type, Geom::Point const &p, Inkscape::Snapper::ConstraintLine const &constraint, bool first_point = true, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; - + void guideSnap(Geom::Point &p, Geom::Point const &guide_normal) const; Inkscape::SnappedPoint freeSnapTranslation(Inkscape::SnapPreferences::PointType point_type, @@ -126,28 +126,28 @@ public: Geom::Point const &s, // s[0] = skew factor, s[1] = scale factor Geom::Point const &o, Geom::Dim2 d) const; - + Inkscape::GuideSnapper guide; ///< guide snapper Inkscape::ObjectSnapper object; ///< snapper to other objects Inkscape::SnapPreferences snapprefs; SnapperList getSnappers() const; SnapperList getGridSnappers() const; - + SPDesktop const *getDesktop() const {return _desktop;} SPNamedView const *getNamedView() const {return _named_view;} SPDocument *getDocument() const; - + protected: SPNamedView const *_named_view; private: - std::vector *_items_to_ignore; + std::vector *_items_to_ignore; SPItem const *_item_to_ignore; SPDesktop const *_desktop; bool _snapindicator; - std::vector *_unselected_nodes; - + std::vector *_unselected_nodes; + Inkscape::SnappedPoint _snapTransformed(Inkscape::SnapPreferences::PointType type, std::vector const &points, Geom::Point const &pointer, @@ -158,16 +158,16 @@ private: Geom::Point const &origin, Geom::Dim2 dim, bool uniform) const; - - Geom::Point _transformPoint(Geom::Point const &p, - Transformation const transformation_type, - Geom::Point const &transformation, - Geom::Point const &origin, - Geom::Dim2 const dim, - bool const uniform) const; - - void _displaySnapsource(Inkscape::SnapPreferences::PointType point_type, Geom::Point const &p) const; - + + Geom::Point _transformPoint(Geom::Point const &p, + Transformation const transformation_type, + Geom::Point const &transformation, + Geom::Point const &origin, + Geom::Dim2 const dim, + bool const uniform) const; + + void _displaySnapsource(Inkscape::SnapPreferences::PointType point_type, Geom::Point const &p) const; + Inkscape::SnappedPoint findBestSnap(Geom::Point const &p, SnappedConstraints &sc, bool constrained) const; }; diff --git a/src/snapped-curve.cpp b/src/snapped-curve.cpp index 7b65c5c65..fa2d561cd 100644 --- a/src/snapped-curve.cpp +++ b/src/snapped-curve.cpp @@ -63,7 +63,7 @@ Inkscape::SnappedPoint Inkscape::SnappedCurve::intersect(SnappedCurve const &cur // There might be multiple intersections: find the closest Geom::Coord best_dist = NR_HUGE; Geom::Point best_p = Geom::Point(NR_HUGE, NR_HUGE); - for (std::vector::const_iterator i = cs.begin(); i != cs.end(); i++) { + for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) { Geom::Point p_ix = this->_curve->pointAt((*i).ta); Geom::Coord dist = Geom::distance(p_ix, p); if (dist < best_dist) { diff --git a/src/snapper.cpp b/src/snapper.cpp index 3dd58737e..492e4ca4d 100644 --- a/src/snapper.cpp +++ b/src/snapper.cpp @@ -19,8 +19,8 @@ * \param nv Named view. * \param d Snap tolerance. */ -Inkscape::Snapper::Snapper(SnapManager const *sm, Geom::Coord const t) : - _snapmanager(sm), +Inkscape::Snapper::Snapper(SnapManager *sm, Geom::Coord const t) : + _snapmanager(sm), _snap_enabled(true), _snapper_tolerance(std::max(t, 1.0)) { diff --git a/src/snapper.h b/src/snapper.h index 0c405fd2b..f6e1a086e 100644 --- a/src/snapper.h +++ b/src/snapper.h @@ -42,9 +42,9 @@ class Snapper { public: Snapper() {} - Snapper(SnapManager const *sm, ::Geom::Coord const t); + Snapper(SnapManager *sm, ::Geom::Coord const t); virtual ~Snapper() {} - + void setSnapperTolerance(Geom::Coord t); Geom::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 @@ -83,10 +83,10 @@ public: Geom::Point getDirection() const { return _direction; } - + void setPoint(Geom::Point const &p) { _point = p; - _has_point = true; + _has_point = true; } private: @@ -105,8 +105,8 @@ public: std::vector const */*it*/) const {}; protected: - SnapManager const *_snapmanager; - + SnapManager *_snapmanager; + bool _snap_enabled; ///< true if this snapper is enabled, otherwise false private: diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 2b7c8bbc4..c46f30e06 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -111,7 +111,7 @@ static void sp_namedview_init(SPNamedView *nv) nv->guides = NULL; nv->viewcount = 0; nv->grids = NULL; - + nv->default_layer_id = 0; nv->connector_spacing = defaultConnSpacing; @@ -248,6 +248,7 @@ static void sp_namedview_build(SPObject *object, SPDocument *document, Inkscape: sp_object_read_attr(object, "inkscape:snap-guide"); 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-intersection-paths"); sp_object_read_attr(object, "inkscape:object-paths"); @@ -469,7 +470,11 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES: nv->snap_manager.snapprefs.setSnapSmoothNodes(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; + break; + case SP_ATTR_INKSCAPE_SNAP_MIDPOINTS: + nv->snap_manager.snapprefs.setSnapMidpoints(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); @@ -501,7 +506,7 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va case SP_ATTR_INKSCAPE_SNAP_PAGE: nv->snap_manager.object.setSnapToPageBorder(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); - break; + break; case SP_ATTR_INKSCAPE_CURRENT_LAYER: nv->default_layer_id = value ? g_quark_from_string(value) : 0; object->requestModified(SP_OBJECT_MODIFIED_FLAG); diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index d3f357e76..7ee2af1ec 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -23,6 +23,7 @@ #include <2geom/rect.h> #include <2geom/transforms.h> #include <2geom/pathvector.h> +#include <2geom/path-intersection.h> #include "helper/geom.h" #include "helper/geom-nodetype.h" @@ -79,21 +80,21 @@ static SPLPEItemClass *parent_class; GType sp_shape_get_type (void) { - static GType type = 0; - if (!type) { - GTypeInfo info = { - sizeof (SPShapeClass), - NULL, NULL, - (GClassInitFunc) sp_shape_class_init, - NULL, NULL, - sizeof (SPShape), - 16, - (GInstanceInitFunc) sp_shape_init, - NULL, /* value_table */ - }; - type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0); - } - return type; + static GType type = 0; + if (!type) { + GTypeInfo info = { + sizeof (SPShapeClass), + NULL, NULL, + (GClassInitFunc) sp_shape_class_init, + NULL, NULL, + sizeof (SPShape), + 16, + (GInstanceInitFunc) sp_shape_init, + NULL, /* value_table */ + }; + type = g_type_register_static (SP_TYPE_LPE_ITEM, "SPShape", &info, (GTypeFlags)0); + } + return type; } /** @@ -112,17 +113,17 @@ sp_shape_class_init (SPShapeClass *klass) gobject_class->finalize = sp_shape_finalize; - sp_object_class->build = sp_shape_build; - sp_object_class->release = sp_shape_release; + sp_object_class->build = sp_shape_build; + sp_object_class->release = sp_shape_release; sp_object_class->set = sp_shape_set; - sp_object_class->update = sp_shape_update; - sp_object_class->modified = sp_shape_modified; + sp_object_class->update = sp_shape_update; + sp_object_class->modified = sp_shape_modified; sp_object_class->write = sp_shape_write; - item_class->bbox = sp_shape_bbox; - item_class->print = sp_shape_print; - item_class->show = sp_shape_show; - item_class->hide = sp_shape_hide; + item_class->bbox = sp_shape_bbox; + item_class->print = sp_shape_print; + item_class->show = sp_shape_show; + item_class->hide = sp_shape_hide; item_class->snappoints = sp_shape_snappoints; lpe_item_class->update_patheffect = NULL; @@ -174,7 +175,7 @@ sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *rep for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { sp_shape_set_marker (object, i, object->style->marker[i].value); - } + } } /** @@ -190,31 +191,31 @@ sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *rep static void sp_shape_release (SPObject *object) { - SPItem *item; - SPShape *shape; - SPItemView *v; - int i; - - item = (SPItem *) object; - shape = (SPShape *) object; - - for (i=SP_MARKER_LOC_START; imarker[i]) { - for (v = item->display; v != NULL; v = v->next) { - sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i); - } + SPItem *item; + SPShape *shape; + SPItemView *v; + int i; + + item = (SPItem *) object; + shape = (SPShape *) object; + + for (i=SP_MARKER_LOC_START; imarker[i]) { + for (v = item->display; v != NULL; v = v->next) { + sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i); + } shape->release_connect[i].disconnect(); shape->modified_connect[i].disconnect(); - shape->marker[i] = sp_object_hunref (shape->marker[i], object); - } - } - if (shape->curve) { - shape->curve = shape->curve->unref(); - } - - if (((SPObjectClass *) parent_class)->release) { - ((SPObjectClass *) parent_class)->release (object); - } + shape->marker[i] = sp_object_hunref (shape->marker[i], object); + } + } + if (shape->curve) { + shape->curve = shape->curve->unref(); + } + + if (((SPObjectClass *) parent_class)->release) { + ((SPObjectClass *) parent_class)->release (object); + } } @@ -237,9 +238,9 @@ sp_shape_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::No return repr; } -/** +/** * Updates the shape when its attributes have changed. Also establishes - * marker objects to match the style settings. + * marker objects to match the style settings. */ static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags) @@ -258,7 +259,7 @@ sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags) */ for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { sp_shape_set_marker (object, i, object->style->marker[i].value); - } + } if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { SPStyle *style; @@ -463,17 +464,17 @@ sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai) static void sp_shape_modified (SPObject *object, unsigned int flags) { - SPShape *shape = SP_SHAPE (object); + SPShape *shape = SP_SHAPE (object); - if (((SPObjectClass *) (parent_class))->modified) { - (* ((SPObjectClass *) (parent_class))->modified) (object, flags); - } + if (((SPObjectClass *) (parent_class))->modified) { + (* ((SPObjectClass *) (parent_class))->modified) (object, flags); + } - if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { - for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) { - nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style); - } - } + if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) { + for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) { + nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style); + } + } } /** @@ -493,7 +494,7 @@ static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const & cbbox.y1 = (*geombbox)[1][1]; if ((SPItem::BBoxType) flags != SPItem::GEOMETRIC_BBOX) { - + SPStyle* style=SP_OBJECT_STYLE (item); if (!style->stroke.isNone()) { double const scale = transform.descrim(); @@ -611,11 +612,11 @@ static void sp_shape_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const & void sp_shape_print (SPItem *item, SPPrintContext *ctx) { - NRRect pbox, dbox, bbox; + NRRect pbox, dbox, bbox; - SPShape *shape = SP_SHAPE(item); + SPShape *shape = SP_SHAPE(item); - if (!shape->curve) return; + if (!shape->curve) return; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); gint add_comments = prefs->getBool("/printing/debug/add-label-comments"); @@ -756,7 +757,7 @@ sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int */ for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { sp_shape_set_marker (object, i, object->style->marker[i].value); - } + } if (sp_shape_has_markers (shape)) { @@ -786,26 +787,26 @@ sp_shape_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int static void sp_shape_hide (SPItem *item, unsigned int key) { - SPShape *shape; - SPItemView *v; - int i; + SPShape *shape; + SPItemView *v; + int i; - shape = (SPShape *) item; + shape = (SPShape *) item; - for (i=0; imarker[i]) { - for (v = item->display; v != NULL; v = v->next) { + for (i=0; imarker[i]) { + for (v = item->display; v != NULL; v = v->next) { if (key == v->key) { - sp_marker_hide ((SPMarker *) shape->marker[i], + sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i); } - } - } - } + } + } + } - if (((SPItemClass *) parent_class)->hide) { - ((SPItemClass *) parent_class)->hide (item, key); - } + if (((SPItemClass *) parent_class)->hide) { + ((SPItemClass *) parent_class)->hide (item, key); + } } /** @@ -884,26 +885,26 @@ sp_shape_number_of_markers (SPShape *shape, int type) static void sp_shape_marker_release (SPObject *marker, SPShape *shape) { - SPItem *item; - int i; - - item = (SPItem *) shape; - - for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) { - if (marker == shape->marker[i]) { - SPItemView *v; - /* Hide marker */ - for (v = item->display; v != NULL; v = v->next) { - sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i); - /* fixme: Do we need explicit remove here? (Lauris) */ - /* nr_arena_item_set_mask (v->arenaitem, NULL); */ - } - /* Detach marker */ + SPItem *item; + int i; + + item = (SPItem *) shape; + + for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) { + if (marker == shape->marker[i]) { + SPItemView *v; + /* Hide marker */ + for (v = item->display; v != NULL; v = v->next) { + sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i); + /* fixme: Do we need explicit remove here? (Lauris) */ + /* nr_arena_item_set_mask (v->arenaitem, NULL); */ + } + /* Detach marker */ shape->release_connect[i].disconnect(); shape->modified_connect[i].disconnect(); - shape->marker[i] = sp_object_hunref (shape->marker[i], item); - } - } + shape->marker[i] = sp_object_hunref (shape->marker[i], item); + } + } } /** @@ -912,12 +913,12 @@ sp_shape_marker_release (SPObject *marker, SPShape *shape) static void sp_shape_marker_modified (SPObject */*marker*/, guint /*flags*/, SPItem */*item*/) { - /* I think mask does update automagically */ - /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */ + /* I think mask does update automagically */ + /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */ } /** - * Adds a new marker to shape object at the location indicated by key. value + * Adds a new marker to shape object at the location indicated by key. value * must be a valid URI reference resolvable from the shape object (i.e., present * in the document ). If the shape object already has a marker * registered at the given position, it is removed first. Then the @@ -971,12 +972,12 @@ sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value) void sp_shape_set_shape (SPShape *shape) { - g_return_if_fail (shape != NULL); - g_return_if_fail (SP_IS_SHAPE (shape)); + g_return_if_fail (shape != NULL); + g_return_if_fail (SP_IS_SHAPE (shape)); - if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) { - SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape); - } + if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) { + SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape); + } } /** @@ -988,16 +989,16 @@ sp_shape_set_shape (SPShape *shape) void sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner) { - if (shape->curve) { - shape->curve = shape->curve->unref(); - } - if (curve) { - if (owner) { - shape->curve = curve->ref(); - } else { - shape->curve = curve->copy(); - } - } + if (shape->curve) { + shape->curve = shape->curve->unref(); + } + if (curve) { + if (owner) { + shape->curve = curve->ref(); + } else { + shape->curve = curve->copy(); + } + } SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } @@ -1007,10 +1008,10 @@ sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner) SPCurve * sp_shape_get_curve (SPShape *shape) { - if (shape->curve) { - return shape->curve->copy(); - } - return NULL; + if (shape->curve) { + return shape->curve->copy(); + } + return NULL; } /** @@ -1019,16 +1020,16 @@ sp_shape_get_curve (SPShape *shape) void sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner) { - if (shape->curve) { - shape->curve = shape->curve->unref(); - } - if (curve) { - if (owner) { - shape->curve = curve->ref(); - } else { - shape->curve = curve->copy(); - } - } + if (shape->curve) { + shape->curve = shape->curve->unref(); + } + if (curve) { + if (owner) { + shape->curve = curve->ref(); + } else { + shape->curve = curve->copy(); + } + } } /** @@ -1058,22 +1059,44 @@ static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p, Inkscape:: while (curve_it2 != path_it->end_closed()) { /* Test whether to add the node between curve_it1 and curve_it2. - * Loop to end_closed (so always including closing segment), the last node to be added - * is the node between the closing segment and the segment before that one. Regardless - * of the path being closed. If the path is closed, the final point was already added by + * Loop to end_closed (so always including closing segment); the last node to be added + * is the node between the closing segment and the segment before that, regardless + * of the path being closed or not. If the path is closed, the final point was already added by * adding the initial point. */ 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 smooths nodes + // 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 = from_2geom(curve_it1->finalPoint() * i2d); } - + + // Consider midpoints of line segments for snapping + if (snapprefs->getSnapMidpoints()) { + if (Geom::LineSegment const* line_segment = dynamic_cast(&(*curve_it1))) { + *p = from_2geom(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()) + if (snapprefs->getSnapIntersectionCS()) { + Geom::Crossings cs; + cs = self_crossings(*path_it); + if (cs.size() > 0) { // There might be multiple intersections... + for (Geom::Crossings::const_iterator i = cs.begin(); i != cs.end(); i++) { + Geom::Point p_ix = (*path_it).pointAt((*i).ta); + *p = from_2geom(p_ix * i2d); + } + } + } } + + + } /* diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index 1bbd20136..713acc23e 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -119,6 +119,7 @@ DocumentProperties::DocumentProperties() //Applies to both nodes and guides, but not to bboxes, that's why its located here _rcbic( _("Rotation _center"), _("Consider the rotation center of an object when snapping"), "inkscape:snap-center", _wr), _rcbsm( _("_Smooth nodes"), _("Snap to smooth nodes too, instead of only snapping to cusp nodes"), "inkscape:snap-smooth-nodes", _wr), + _rcbmp( _("_Midpoints"), _("Snap midpoints of straight line segments"), "inkscape:snap-midpoints", _wr), _rcbsigg(_("_Grid with guides"), _("Snap to grid-guide intersections"), "inkscape:snap-intersection-grid-guide", _wr), _rcbsils(_("_Paths"), _("Snap to intersections of paths ('snap to paths' must be enabled, see the previous tab)"), "inkscape:snap-intersection-paths", _wr), @@ -368,7 +369,7 @@ DocumentProperties::build_snap_dtls() //Other options to locate here: e.g. visual snapping indicators on/off Gtk::Label *label_i= manage (new Gtk::Label); - label_i->set_markup (_("Snapping to intersections of")); + label_i->set_markup (_("Snapping intersections of")); Gtk::Label *label_m = manage (new Gtk::Label); label_m->set_markup (_("Special points to consider")); @@ -380,7 +381,8 @@ DocumentProperties::build_snap_dtls() 0, 0, label_m, 0, 0, &_rcbic, - 0, &_rcbsm + 0, &_rcbsm, + 0, &_rcbmp }; attach_all(_page_snap_dtls.table(), array, G_N_ELEMENTS(array)); @@ -774,6 +776,7 @@ 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()); _rcbsigg.setActive (nv->snap_manager.snapprefs.getSnapIntersectionGG()); _rcbsils.setActive (nv->snap_manager.snapprefs.getSnapIntersectionCS()); _rcbsnop.setActive(nv->snap_manager.object.getSnapToItemPath()); diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index 2428ab24a..36a6c739b 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -112,7 +112,7 @@ protected: RegisteredCheckButton _rcbsnon, _rcbsnbbp, _rcbsnbbn, _rcbsnpb; ToleranceSlider _rsu_sno, _rsu_sn, _rsu_gusn; //--------------------------------------------------------------- - RegisteredCheckButton _rcbic, _rcbsm; + RegisteredCheckButton _rcbic, _rcbsm, _rcbmp; RegisteredCheckButton _rcbsigg, _rcbsils; //--------------------------------------------------------------- Gtk::Menu _menu; -- 2.30.2