From 754eb8f0aee44ed5d465f1f7b49dcecd6787ec82 Mon Sep 17 00:00:00 2001 From: Diederik van Lierop Date: Sun, 7 Nov 2010 21:16:45 +0100 Subject: [PATCH] Leave constrained angular snapping to the snap manager, instead of handling it locally --- src/draw-context.cpp | 50 +++++++++++---------------------- src/gradient-drag.cpp | 64 +++++++++++++++++-------------------------- src/seltrans.cpp | 1 + src/snap.cpp | 56 +++++++++++++++++++++++++++++++++---- src/snap.h | 5 ++++ 5 files changed, 98 insertions(+), 78 deletions(-) diff --git a/src/draw-context.cpp b/src/draw-context.cpp index ca68b3f6d..66a2309b2 100644 --- a/src/draw-context.cpp +++ b/src/draw-context.cpp @@ -42,7 +42,6 @@ #include "sp-namedview.h" #include "live_effects/lpe-patternalongpath.h" #include "style.h" -#include "util/mathfns.h" static void sp_draw_context_class_init(SPDrawContextClass *klass); static void sp_draw_context_init(SPDrawContext *dc); @@ -477,43 +476,26 @@ void spdc_endpoint_snap_rotation(SPEventContext const *const ec, Geom::Point &p, { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned const snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); + SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; + m.setup(SP_EVENT_CONTEXT_DESKTOP(ec)); - if (snaps > 0) { // 0 means no snapping - // p is at an arbitrary angle. Now we should snap this angle to specific increments. - // For this we'll calculate the closest two angles, one at each side of the current angle - Geom::Line y_axis(Geom::Point(0, 0), Geom::Point(0, 1)); - Geom::Line p_line(o, p); - double angle = Geom::angle_between(y_axis, p_line); - double angle_incr = M_PI / snaps; - double angle_ceil = round_to_upper_multiple_plus(angle, angle_incr); - double angle_floor = round_to_lower_multiple_plus(angle, angle_incr); - // We have to angles now. The constrained snapper will try each of them and return the closest - // But first we should setup the snapper - - SnapManager &m = SP_EVENT_CONTEXT_DESKTOP(ec)->namedview->snap_manager; - m.setup(SP_EVENT_CONTEXT_DESKTOP(ec)); - bool snap_enabled = m.snapprefs.getSnapEnabledGlobally(); - if (state & GDK_SHIFT_MASK) { - // SHIFT disables all snapping, except the angular snapping. After all, the user explicitly asked for angular - // snapping by pressing CTRL, otherwise we wouldn't have arrived here. But although we temporarily disable - // the snapping here, we must still call for a constrained snap in order to apply the constraints (i.e. round - // to the nearest angle increment) - m.snapprefs.setSnapEnabledGlobally(false); - } - - // Now do the snapping... - std::vector constraints; - constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_ceil - M_PI/2))); - constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_floor - M_PI/2))); + bool snap_enabled = m.snapprefs.getSnapEnabledGlobally(); + if (state & GDK_SHIFT_MASK) { + // SHIFT disables all snapping, except the angular snapping. After all, the user explicitly asked for angular + // snapping by pressing CTRL, otherwise we wouldn't have arrived here. But although we temporarily disable + // the snapping here, we must still call for a constrained snap in order to apply the constraints (i.e. round + // to the nearest angle increment) + m.snapprefs.setSnapEnabledGlobally(false); + } - Inkscape::SnappedPoint sp = m.multipleConstrainedSnaps(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE), constraints); - p = sp.getPoint(); + Inkscape::SnappedPoint dummy = m.constrainedAngularSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_NODE_HANDLE), boost::optional(), o, snaps); + p = dummy.getPoint(); - m.unSetup(); - if (state & GDK_SHIFT_MASK) { - m.snapprefs.setSnapEnabledGlobally(snap_enabled); // restore the original setting - } + if (state & GDK_SHIFT_MASK) { + m.snapprefs.setSnapEnabledGlobally(snap_enabled); // restore the original setting } + + m.unSetup(); } diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp index 32aa7c084..d5ab64794 100644 --- a/src/gradient-drag.cpp +++ b/src/gradient-drag.cpp @@ -568,22 +568,6 @@ SPObject *GrDraggable::getServer() return server; } -static -boost::optional -get_snap_vector (Geom::Point p, Geom::Point o, double snap, double initial) -{ - double r = L2 (p - o); - if (r < 1e-3) { - return boost::optional(); - } - - double angle = atan2 (p - o); - // snap angle to snaps increments, starting from initial: - double a_snapped = initial + floor((angle - initial)/snap + 0.5) * snap; - // calculate the new position and subtract p to get the vector: - return (o + r * Geom::Point(cos(a_snapped), sin(a_snapped)) - p); -} - static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gpointer data) { @@ -645,15 +629,17 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp } } - m.setup(desktop); if (!((state & GDK_SHIFT_MASK) || (state & GDK_CONTROL_MASK))) { + m.setup(desktop); Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE)); + m.unSetup(); if (s.getSnapped()) { p = s.getPoint(); sp_knot_moveto (knot, p); } } else if (state & GDK_CONTROL_MASK) { SnappedConstraints sc; + Inkscape::SnapCandidatePoint scp = Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); unsigned snaps = abs(prefs->getInt("/options/rotationsnapsperpi/value", 12)); /* 0 means no snapping. */ @@ -699,40 +685,40 @@ gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, guint state, gp dr_snap = dragger->point_original; } - boost::optional snap_vector; + // dr_snap contains the origin of the gradient, whereas p will be the new endpoint which we will try to snap now + Inkscape::SnappedPoint sp; if (dr_snap.isFinite()) { + m.setup(desktop); if (state & GDK_MOD1_MASK) { // with Alt, snap to the original angle and its perpendiculars - snap_vector = get_snap_vector (p, dr_snap, M_PI/2, Geom::atan2 (dragger->point_original - dr_snap)); + sp = m.constrainedAngularSnap(scp, dragger->point_original, dr_snap, 2); } else { // with Ctrl, snap to M_PI/snaps - snap_vector = get_snap_vector (p, dr_snap, M_PI/snaps, 0); - } - if (snap_vector) { - Inkscape::Snapper::SnapConstraint cl(dr_snap, p + *snap_vector - dr_snap); - Inkscape::SnappedPoint s = m.constrainedSnap(Inkscape::SnapCandidatePoint(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE), cl); - if (s.getSnapped()) { - s.setTransformation(s.getPoint() - p); - sc.points.push_back(s); - } else { - Inkscape::SnappedPoint dummy(p + *snap_vector, Inkscape::SNAPSOURCE_OTHER_HANDLE, 0, Inkscape::SNAPTARGET_CONSTRAINED_ANGLE, Geom::L2(*snap_vector), 10000, true, true, false); - dummy.setTransformation(*snap_vector); - sc.points.push_back(dummy); - } + sp = m.constrainedAngularSnap(scp, boost::optional(), dr_snap, snaps); } + m.unSetup(); + sc.points.push_back(sp); } } - Inkscape::SnappedPoint bsp = m.findBestSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE), sc, true); // snap indicator will be displayed if needed - - if (bsp.getSnapped()) { - p += bsp.getTransformation(); - sp_knot_moveto (knot, p); + m.setup(desktop, false); // turn of the snap indicator temporarily + Inkscape::SnappedPoint bsp = m.findBestSnap(scp, sc, true); + m.unSetup(); + if (!bsp.getSnapped()) { + // If we didn't truly snap to an object or to a grid, then we will still have to look for the + // closest projection onto one of the constraints. findBestSnap() will not do this for us + for (std::list::const_iterator i = sc.points.begin(); i != sc.points.end(); i++) { + if (i == sc.points.begin() || (Geom::L2((*i).getPoint() - p) < Geom::L2(bsp.getPoint() - p))) { + bsp.setPoint((*i).getPoint()); + bsp.setTarget(Inkscape::SNAPTARGET_CONSTRAINED_ANGLE); + } + } } + //p = sc.points.front().getPoint(); + p = bsp.getPoint(); + sp_knot_moveto (knot, p); } - m.unSetup(); - drag->keep_selection = (bool) g_list_find(drag->selected, dragger); bool scale_radial = (state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK); diff --git a/src/seltrans.cpp b/src/seltrans.cpp index 9a1fdf4ad..7ea2a86c0 100644 --- a/src/seltrans.cpp +++ b/src/seltrans.cpp @@ -1656,6 +1656,7 @@ void Inkscape::SelTrans::_keepClosestPointOnly(std::vectorsnapindicator->set_new_snaptarget(result); - } if (snap_mouse) { - // We still have to apply the constraint, because so far we only tried a freeSnap + // If "snap_mouse" then we still have to apply the constraint, because so far we only tried a freeSnap Geom::Point result_closest; for (std::vector::const_iterator c = constraints.begin(); c != constraints.end(); c++) { // Project the mouse pointer onto the constraint; In case we don't snap then we will @@ -506,6 +503,55 @@ Inkscape::SnappedPoint SnapManager::multipleConstrainedSnaps(Inkscape::SnapCandi return no_snap; } +/** + * \brief Try to snap a point to something at a specific angle + * + * When drawing a straight line or modifying a gradient, it will snap to specific angle increments + * if CTRL is being pressed. This method will enforce this angular constraint (even if there is nothing + * to snap to) + * + * \param p Source point to be snapped + * \param p_ref Optional original point, relative to which the angle should be calculated. If empty then + * the angle will be calculated relative to the y-axis + * \param snaps Number of angular increments per PI radians; E.g. if snaps = 2 then we will snap every PI/2 = 90 degrees + */ + +Inkscape::SnappedPoint SnapManager::constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p, + boost::optional const &p_ref, + Geom::Point const &o, + unsigned const snaps) const +{ + Inkscape::SnappedPoint sp; + if (snaps > 0) { // 0 means no angular snapping + // p is at an arbitrary angle. Now we should snap this angle to specific increments. + // For this we'll calculate the closest two angles, one at each side of the current angle + Geom::Line y_axis(Geom::Point(0, 0), Geom::Point(0, 1)); + Geom::Line p_line(o, p.getPoint()); + double angle = Geom::angle_between(y_axis, p_line); + double angle_incr = M_PI / snaps; + double angle_offset = 0; + if (p_ref) { + Geom::Line p_line_ref(o, *p_ref); + angle_offset = Geom::angle_between(y_axis, p_line_ref); + } + double angle_ceil = round_to_upper_multiple_plus(angle, angle_incr, angle_offset); + double angle_floor = round_to_lower_multiple_plus(angle, angle_incr, angle_offset); + // We have two angles now. The constrained snapper will try each of them and return the closest + + // Now do the snapping... + std::vector constraints; + constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_ceil - M_PI/2))); + constraints.push_back(Inkscape::Snapper::SnapConstraint(Geom::Line(o, angle_floor - M_PI/2))); + sp = multipleConstrainedSnaps(p, constraints); // Constraints will always be applied, even if we didn't snap + if (!sp.getSnapped()) { // If we haven't snapped then we only had the constraint applied; + sp.setTarget(Inkscape::SNAPTARGET_CONSTRAINED_ANGLE); + } + } else { + sp = freeSnap(p); + } + return sp; +} + /** * \brief Try to snap a point of a guide to another guide or to a node * diff --git a/src/snap.h b/src/snap.h index 0f27017a5..3c9af7d51 100644 --- a/src/snap.h +++ b/src/snap.h @@ -138,6 +138,11 @@ public: std::vector const &constraints, Geom::OptRect const &bbox_to_snap = Geom::OptRect()) const; + Inkscape::SnappedPoint constrainedAngularSnap(Inkscape::SnapCandidatePoint const &p, + boost::optional const &p_ref, + Geom::Point const &o, + unsigned const snaps) const; + void guideFreeSnap(Geom::Point &p, Geom::Point const &guide_normal, SPGuideDragType drag_type) const; void guideConstrainedSnap(Geom::Point &p, SPGuide const &guideline) const; -- 2.30.2