From 4e33f020dd832c0fa662afd97a40d6eba7f1fade Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Wed, 14 Nov 2007 19:53:05 +0000 Subject: [PATCH] Snap to intersections of line segments --- src/attributes.cpp | 2 + src/attributes.h | 2 + src/livarot/Path.cpp | 14 ++++++ src/livarot/Path.h | 2 + src/object-snapper.cpp | 69 +++++++++++++++------------ src/object-snapper.h | 13 ++--- src/snap.cpp | 32 ++++++++++--- src/snap.h | 17 +++++-- src/snapped-line.cpp | 63 +++++++++++++++++++----- src/snapped-line.h | 2 + src/sp-namedview.cpp | 10 ++++ src/ui/dialog/document-properties.cpp | 17 +++++-- src/ui/dialog/document-properties.h | 5 +- 13 files changed, 185 insertions(+), 63 deletions(-) diff --git a/src/attributes.cpp b/src/attributes.cpp index f09e9cc36..a23621c10 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -88,6 +88,8 @@ static SPStyleProp const props[] = { {SP_ATTR_INKSCAPE_SNAP_NODES, "inkscape:snap-nodes"}, {SP_ATTR_INKSCAPE_SNAP_GUIDE, "inkscape:snap-guide"}, {SP_ATTR_INKSCAPE_SNAP_CENTER, "inkscape:snap-center"}, + {SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, "inkscape:snap-intersection-grid-guide"}, + {SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM, "inkscape:snap-intersection-line-segments"}, {SP_ATTR_INKSCAPE_OBJECT_PATHS, "inkscape:object-paths"}, {SP_ATTR_INKSCAPE_OBJECT_NODES, "inkscape:object-nodes"}, {SP_ATTR_INKSCAPE_BBOX_PATHS, "inkscape:bbox-paths"}, diff --git a/src/attributes.h b/src/attributes.h index 768fd0003..4f222bba1 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -89,6 +89,8 @@ enum SPAttributeEnum { SP_ATTR_INKSCAPE_SNAP_NODES, SP_ATTR_INKSCAPE_SNAP_GUIDE, SP_ATTR_INKSCAPE_SNAP_CENTER, + SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE, + SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM, SP_ATTR_INKSCAPE_OBJECT_PATHS, SP_ATTR_INKSCAPE_OBJECT_NODES, SP_ATTR_INKSCAPE_BBOX_PATHS, diff --git a/src/livarot/Path.cpp b/src/livarot/Path.cpp index 8ad806349..726b0001c 100644 --- a/src/livarot/Path.cpp +++ b/src/livarot/Path.cpp @@ -908,6 +908,20 @@ char *Path::svg_dump_path() const return g_strdup (os.str().c_str()); } +// Find out if the segment that corresponds to 'piece' is a straight line +bool Path::IsLineSegment(int piece) +{ + if (piece < 0 || piece >= int(descr_cmd.size())) { + return false; + } + + PathDescr const *theD = descr_cmd[piece]; + int const typ = theD->getType(); + + return (typ == descr_lineto); +} + + /* Local Variables: mode:c++ diff --git a/src/livarot/Path.h b/src/livarot/Path.h index a405e00cc..b6e563d98 100644 --- a/src/livarot/Path.h +++ b/src/livarot/Path.h @@ -213,6 +213,8 @@ public: void Affiche(); char *svg_dump_path() const; + + bool IsLineSegment(int piece); private: // utilitary functions for the path contruction diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp index 67b465d03..58ef71595 100644 --- a/src/object-snapper.cpp +++ b/src/object-snapper.cpp @@ -113,8 +113,8 @@ void Inkscape::ObjectSnapper::_findCandidates(SPObject* r, } -bool Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, - Inkscape::SnappedPoint &s, +void Inkscape::ObjectSnapper::_snapNodes(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point, DimensionToSnap const snap_dim) const @@ -169,10 +169,11 @@ bool Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, } //Do the snapping, using all the nodes and corners collected above + NR::Point snapped_point; + SnappedPoint s; for (std::vector::const_iterator k = _points_to_snap_to->begin(); k != _points_to_snap_to->end(); k++) { /* Try to snap to this node of the path */ NR::Coord dist = NR_HUGE; - NR::Point snapped_point; switch (snap_dim) { case SNAP_X: dist = fabs((*k)[NR::X] - p[NR::X]); @@ -194,12 +195,14 @@ bool Inkscape::ObjectSnapper::_snapNodes(Inkscape::Snapper::PointType const &t, } } - return success; + if (success) { + sc.points.push_back(s); + } } -bool Inkscape::ObjectSnapper::_snapPaths(Inkscape::Snapper::PointType const &t, - Inkscape::SnappedPoint &s, +void Inkscape::ObjectSnapper::_snapPaths(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point) const { @@ -297,6 +300,7 @@ bool Inkscape::ObjectSnapper::_snapPaths(Inkscape::Snapper::PointType const &t, } //Now we can finally do the real snapping, using the paths collected above + SnappedPoint s; for (std::vector::const_iterator k = _paths_to_snap_to->begin(); k != _paths_to_snap_to->end(); k++) { if (*k) { if (first_point) { @@ -309,18 +313,34 @@ bool Inkscape::ObjectSnapper::_snapPaths(Inkscape::Snapper::PointType const &t, /* Convert the nearest point back to desktop coordinates */ NR::Point const o_it = get_point_on_Path(*k, o->piece, o->t); - NR::Point const o_dt = desktop->doc2dt(o_it); - + NR::Point const o_dt = desktop->doc2dt(o_it); NR::Coord const dist = NR::L2(o_dt - p); - if (dist < getDistance() && dist < s.getDistance()) { - s = SnappedPoint(o_dt, dist); - success = true; + + if (dist < getDistance()) { + // if we snap to a straight line segment (within a path), then return this line segment + if ((*k)->IsLineSegment(o->piece)) { + NR::Point start_point; + NR::Point end_point; + (*k)->PointAt(o->piece, 0, start_point); + (*k)->PointAt(o->piece, 1, end_point); + start_point = desktop->doc2dt(start_point); + end_point = desktop->doc2dt(end_point); + sc.lines.push_back(Inkscape::SnappedLineSegment(o_dt, dist, start_point, end_point)); + } else { + // for segments other than straight lines of a path, we'll return just the closest snapped point + if (dist < s.getDistance()) { + s = SnappedPoint(o_dt, dist); + success = true; + } + } } } } } - return success; + if (success) { + sc.points.push_back(s); + } } @@ -340,19 +360,11 @@ void Inkscape::ObjectSnapper::_doFreeSnap(SnappedConstraints &sc, _findCandidates(sp_document_root(_named_view->document), it, first_point, points_to_snap, SNAP_XY); } - SnappedPoint s(p, NR_HUGE); - bool snapped_to_node = false; - bool snapped_to_path = false; - if (_snap_to_itemnode || _snap_to_bboxnode) { - snapped_to_node = _snapNodes(t, s, p, first_point, SNAP_XY); + _snapNodes(sc, t, p, first_point, SNAP_XY); } if (_snap_to_itempath || _snap_to_bboxpath) { - snapped_to_path = _snapPaths(t, s, p, first_point); - } - - if (snapped_to_node || snapped_to_path) { - sc.points.push_back(s); + _snapPaths(sc, t, p, first_point); } } @@ -374,11 +386,12 @@ void Inkscape::ObjectSnapper::_doConstrainedSnap( SnappedConstraints &sc, -Inkscape::SnappedPoint Inkscape::ObjectSnapper::guideSnap(NR::Point const &p, - DimensionToSnap const snap_dim) const +void Inkscape::ObjectSnapper::guideSnap(SnappedConstraints &sc, + NR::Point const &p, + DimensionToSnap const snap_dim) const { if ( NULL == _named_view ) { - return SnappedPoint(p, NR_HUGE); + return; } /* Get a list of all the SPItems that we will try to snap to */ @@ -389,11 +402,7 @@ Inkscape::SnappedPoint Inkscape::ObjectSnapper::guideSnap(NR::Point const &p, points_to_snap.push_back(p); _findCandidates(sp_document_root(_named_view->document), it, true, points_to_snap, snap_dim); - - SnappedPoint s(p, NR_HUGE); - _snapNodes(Inkscape::Snapper::SNAPPOINT_GUIDE, s, p, true, snap_dim); - - return s; + _snapNodes(sc, Inkscape::Snapper::SNAPPOINT_GUIDE, p, true, snap_dim); } /** diff --git a/src/object-snapper.h b/src/object-snapper.h index 4785ca431..b3bf481ad 100644 --- a/src/object-snapper.h +++ b/src/object-snapper.h @@ -77,8 +77,9 @@ public: _strict_snapping = enabled; } - SnappedPoint guideSnap(NR::Point const &p, - DimensionToSnap const snap_dim) const; + void guideSnap(SnappedConstraints &sc, + NR::Point const &p, + DimensionToSnap const snap_dim) const; bool ThisSnapperMightSnap() const; @@ -108,14 +109,14 @@ private: std::vector &points_to_snap, DimensionToSnap const snap_dim) const; - bool _snapNodes(Inkscape::Snapper::PointType const &t, - Inkscape::SnappedPoint &s, + void _snapNodes(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point, DimensionToSnap const snap_dim) const; - bool _snapPaths(Inkscape::Snapper::PointType const &t, - Inkscape::SnappedPoint &s, + void _snapPaths(SnappedConstraints &sc, + Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &first_point) const; diff --git a/src/snap.cpp b/src/snap.cpp index 4e4e04844..edc30e21a 100644 --- a/src/snap.cpp +++ b/src/snap.cpp @@ -104,7 +104,7 @@ bool SnapManager::SomeSnapperMightSnap() const /* * The snappers have too many parameters to adjust individually. Therefore only * 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, + * 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 * methods defined below: setSnapModeBBox() and setSnapModeNode(). * @@ -340,7 +340,10 @@ Inkscape::SnappedPoint SnapManager::guideSnap(NR::Point const &p, snap_dim = Inkscape::ObjectSnapper::SNAP_XY; } - return object.guideSnap(p, snap_dim); + SnappedConstraints sc; + object.guideSnap(sc, p, snap_dim); + + return findBestSnap(p, sc); } @@ -685,6 +688,20 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedCons sp_list.push_back(std::make_pair(closestPoint, NR_HUGE)); } + // search for the closest snapped line segment + Inkscape::SnappedLineSegment closestLineSegment; + if (getClosestSLS(sc.lines, closestLineSegment)) { + sp_list.push_back(std::make_pair(Inkscape::SnappedPoint(closestLineSegment), NR_HUGE)); + } + + if (_intersectionLS) { + // search for the closest snapped intersection of line segments + Inkscape::SnappedPoint closestLineSegmentIntersection; + if (getClosestIntersectionSLS(sc.lines, closestLineSegmentIntersection)) { + sp_list.push_back(std::make_pair(closestLineSegmentIntersection, NR_HUGE)); + } + } + // search for the closest snapped grid line Inkscape::SnappedLine closestGridLine; if (getClosestSL(sc.grid_lines, closestGridLine)) { @@ -710,13 +727,15 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedCons } // search for the closest snapped intersection of grid with guide lines - Inkscape::SnappedPoint closestGridGuidePoint; - if (getClosestIntersectionSL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) { - sp_list.push_back(std::make_pair(closestGridGuidePoint, std::min(guide_sens, grid_sens))); + if (_intersectionGG) { + Inkscape::SnappedPoint closestGridGuidePoint; + if (getClosestIntersectionSL(sc.grid_lines, sc.guide_lines, closestGridGuidePoint)) { + sp_list.push_back(std::make_pair(closestGridGuidePoint, std::min(guide_sens, grid_sens))); + } } // now let's see which snapped point gets a thumbs up - Inkscape::SnappedPoint bestPoint(p, NR_HUGE); + Inkscape::SnappedPoint bestPoint(p, NR_HUGE); for (std::list >::const_iterator i = sp_list.begin(); i != sp_list.end(); i++) { // first find out if this snapped point is within snapping range if ((*i).first.getDistance() <= (*i).second) { @@ -734,6 +753,7 @@ Inkscape::SnappedPoint SnapManager::findBestSnap(NR::Point const &p, SnappedCons } return bestPoint; } + /* Local Variables: mode:c++ diff --git a/src/snap.h b/src/snap.h index b7a6151ad..88254430c 100644 --- a/src/snap.h +++ b/src/snap.h @@ -118,6 +118,10 @@ public: NR::Coord const &s, NR::Point const &o, NR::Dim2 d) const; + + Inkscape::SnappedPoint guideSnap(NR::Point const &p, + Inkscape::ObjectSnapper::DimensionToSnap const snap_dim) const; + Inkscape::GuideSnapper guide; ///< guide snapper Inkscape::ObjectSnapper object; ///< snapper to other objects @@ -131,12 +135,17 @@ public: bool getSnapModeBBox() const; bool getSnapModeNode() const; bool getSnapModeGuide() const; + + void setSnapIntersectionGG(bool enabled) {_intersectionGG = enabled;} + void setSnapIntersectionLS(bool enabled) {_intersectionLS = enabled;} + bool getSnapIntersectionGG() { return _intersectionGG;} + bool getSnapIntersectionLS() { return _intersectionLS;} void setIncludeItemCenter(bool enabled) { _include_item_center = enabled; - object.setIncludeItemCenter(enabled); //store a local copy in the object-snapper - //instead of passing it through many functions - } + // also store a local copy in the object-snapper instead of passing it through many functions + object.setIncludeItemCenter(enabled); + } bool getIncludeItemCenter() const { return _include_item_center; @@ -155,6 +164,8 @@ private: }; bool _include_item_center; //If true, snapping nodes will also snap the item's center + bool _intersectionGG; + bool _intersectionLS; std::pair _snapTransformed(Inkscape::Snapper::PointType type, std::vector const &points, diff --git a/src/snapped-line.cpp b/src/snapped-line.cpp index c507108ac..7ae94e4f4 100644 --- a/src/snapped-line.cpp +++ b/src/snapped-line.cpp @@ -36,21 +36,21 @@ Inkscape::SnappedLineSegment::~SnappedLineSegment() Inkscape::SnappedPoint Inkscape::SnappedLineSegment::intersect(SnappedLineSegment const &line) const { - //TODO: Diederik, implement the intersection - NR::Point const intersection = NR::Point(NR_HUGE, NR_HUGE); + Geom::Point intersection_2geom(NR_HUGE, NR_HUGE); + NR::Coord distance = NR_HUGE; - //if (result == INTERSECTS) { + Geom::IntersectorKind result = segment_intersect(_start_point_of_line.to_2geom(), _end_point_of_line.to_2geom(), + line._start_point_of_line.to_2geom(), line._end_point_of_line.to_2geom(), + intersection_2geom); + NR::Point intersection(intersection_2geom); + + if (result == Geom::intersects) { /* The relevant snapped distance is the distance to the closest snapped line, not the - distance to the intersection. For example, when a box is almost aligned with a grid - in both horizontal and vertical directions, the distance to the intersection of the - grid lines will always be larger then the distance to a grid line. We will be snapping - to the closest snapped point however, so if we ever want to snap to the intersection - then the distance to it should at least be equal to the other distance, not greater - than it, as that would rule the intersection out + distance to the intersection. See the comment in Inkscape::SnappedLine::intersect */ - NR::Coord distance = std::min(_distance, line.getDistance()); - //} - return SnappedPoint(intersection, distance); + distance = std::min(_distance, line.getDistance()); + } + return SnappedPoint(intersection, distance, result == Geom::intersects); }; @@ -98,12 +98,49 @@ Inkscape::SnappedPoint Inkscape::SnappedLine::intersect(SnappedLine const &line) than it, as that would rule the intersection out */ distance = std::min(_distance, line.getDistance()); - //std::cout << "Intersected nicely, now getSIL distance = " << distance << std::endl; } return SnappedPoint(intersection, distance, result == Geom::intersects); } +// search for the closest snapped line segment +bool getClosestSLS(std::list &list, Inkscape::SnappedLineSegment &result) +{ + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + if ((i == list.begin()) || (*i).getDistance() < result.getDistance()) { + result = *i; + success = true; + } + } + + return success; +} + +// search for the closest intersection of two snapped line segments, which are both member of the same collection +bool getClosestIntersectionSLS(std::list &list, Inkscape::SnappedPoint &result) +{ + bool success = false; + + for (std::list::const_iterator i = list.begin(); i != list.end(); i++) { + std::list::const_iterator j = i; + j++; + for (; j != list.end(); j++) { + Inkscape::SnappedPoint sp = (*i).intersect(*j); + if (sp.getAtIntersection()) { + if (!success || sp.getDistance() < result.getDistance()) { + // !success because the first intersection cannot be compared to a previous one + result = sp; + success = true; + } + } + } + } + + return success; +} + // search for the closest snapped line bool getClosestSL(std::list &list, Inkscape::SnappedLine &result) { diff --git a/src/snapped-line.h b/src/snapped-line.h index 3fed89d7d..be2a792b7 100644 --- a/src/snapped-line.h +++ b/src/snapped-line.h @@ -57,6 +57,8 @@ private: } +bool getClosestSLS(std::list &list, Inkscape::SnappedLineSegment &result); +bool getClosestIntersectionSLS(std::list &list, Inkscape::SnappedPoint &result); bool getClosestSL(std::list &list, Inkscape::SnappedLine &result); bool getClosestIntersectionSL(std::list &list, Inkscape::SnappedPoint &result); bool getClosestIntersectionSL(std::list &list1, std::list &list2, Inkscape::SnappedPoint &result); diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index 9fba7bc99..03cc4bdad 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -158,6 +158,8 @@ static void sp_namedview_build(SPObject *object, SPDocument *document, Inkscape: sp_object_read_attr(object, "inkscape:snap-nodes"); sp_object_read_attr(object, "inkscape:snap-guide"); sp_object_read_attr(object, "inkscape:snap-center"); + sp_object_read_attr(object, "inkscape:snap-intersection-grid-guide"); + sp_object_read_attr(object, "inkscape:snap-snap-intersection-line-segments"); sp_object_read_attr(object, "inkscape:object-paths"); sp_object_read_attr(object, "inkscape:object-nodes"); sp_object_read_attr(object, "inkscape:bbox-paths"); @@ -371,6 +373,14 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va nv->snap_manager.setSnapModeGuide(value ? sp_str_to_bool(value) : FALSE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; + case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE: + nv->snap_manager.setSnapIntersectionGG(value ? sp_str_to_bool(value) : FALSE); + object->requestModified(SP_OBJECT_MODIFIED_FLAG); + break; + case SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM: + nv->snap_manager.setSnapIntersectionLS(value ? sp_str_to_bool(value) : FALSE); + 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); object->requestModified(SP_OBJECT_MODIFIED_FLAG); diff --git a/src/ui/dialog/document-properties.cpp b/src/ui/dialog/document-properties.cpp index f8192dc8c..88e9f2bf2 100644 --- a/src/ui/dialog/document-properties.cpp +++ b/src/ui/dialog/document-properties.cpp @@ -359,27 +359,36 @@ void DocumentProperties::build_snap_dtls() { _page_snap_dtls.show(); + + _rcbsigg.init (_("_Grid with guides"), + _("Snap to grid-guide intersections"), + "inkscape:snap-intersection-grid-guide", _wr); + + _rcbsils.init (_("_Line segments"), + _("Snap to intersections of line segments ('snap to paths' must be enabled, see the previous tab)"), + "inkscape:snap-intersection-line-segments", _wr); _rcbsng.init (_("_Snap guides while dragging"), _("While dragging a guide, snap to object nodes or bounding box corners ('snap to nodes' or 'snap to bounding box corners', both on the previous tab, must be enabled)"), "inkscape:snap-guide", _wr); + //Applies to both nodes and guides, but not to bboxes, that's why its located here _rcbic.init (_("_Include the object's rotation center"), _("Also snap the rotation center of an object when snapping nodes or guides"), "inkscape:snap-center", _wr); - //Applies to both nodes and guides, but not to bboxes, that's why its located here - + //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")); + label_i->set_markup (_("Snapping to intersections of")); Gtk::Label *label_m = manage (new Gtk::Label); label_m->set_markup (_("Miscellaneous")); Gtk::Widget *const array[] = { label_i, 0, - 0, 0, + 0, _rcbsigg._button, + 0, _rcbsils._button, 0, 0, label_m, 0, 0, _rcbsng._button, diff --git a/src/ui/dialog/document-properties.h b/src/ui/dialog/document-properties.h index ae8237e3e..07382d524 100644 --- a/src/ui/dialog/document-properties.h +++ b/src/ui/dialog/document-properties.h @@ -74,12 +74,15 @@ protected: RegisteredUnitMenu _rum_gusn; RegisteredColorPicker _rcp_gui, _rcp_hgui; //--------------------------------------------------------------- - RegisteredCheckButton _rcbsnbb, _rcbsng, _rcbsnn, _rcbic, _rcbsnop; + RegisteredCheckButton _rcbsnbb, _rcbsnn, _rcbsnop; RegisteredCheckButton _rcbsnon, _rcbsnbbp, _rcbsnbbn; RegisteredUnitMenu _rumso; ToleranceSlider _rsu_sno, _rsu_sn, _rsu_gusn; RegisteredRadioButtonPair _rrb_pix; //--------------------------------------------------------------- + RegisteredCheckButton _rcbic, _rcbsng; + RegisteredCheckButton _rcbsigg, _rcbsils; + //--------------------------------------------------------------- Gtk::Notebook _grids_notebook; Gtk::Button _grids_button_new; Gtk::Button _grids_button_remove; -- 2.30.2