From 1dcc408c2579584fcea4bd1602f7c8d41c27dfb8 Mon Sep 17 00:00:00 2001 From: dvlierop2 Date: Sat, 8 Dec 2007 21:18:49 +0000 Subject: [PATCH] Snap to axonometric grid lines --- src/display/canvas-axonomgrid.cpp | 76 ++++++++++++++++++++++++++----- src/display/canvas-grid.cpp | 9 +++- src/guide-snapper.cpp | 9 +--- src/line-snapper.cpp | 59 ++++++++++++++++++++---- src/line-snapper.h | 26 ++++++----- src/sp-namedview.cpp | 4 +- 6 files changed, 140 insertions(+), 43 deletions(-) diff --git a/src/display/canvas-axonomgrid.cpp b/src/display/canvas-axonomgrid.cpp index a74151523..e16e1fdf2 100644 --- a/src/display/canvas-axonomgrid.cpp +++ b/src/display/canvas-axonomgrid.cpp @@ -22,10 +22,10 @@ #include "sp-canvas-util.h" #include "canvas-axonomgrid.h" #include "util/mathfns.h" +#include "2geom/geom.h" #include "display-forward.h" #include - #include "canvas-grid.h" #include "desktop-handles.h" #include "helper/units.h" @@ -619,20 +619,74 @@ CanvasAxonomGridSnapper::_getSnapLines(NR::Point const &p) const } /* This is to make sure we snap to only visible grid lines */ - double scaled_spacing = grid->spacing_ylines; // this is spacing of visible lines if screen pixels + double scaled_spacing_h = grid->spacing_ylines; // this is spacing of visible lines if screen pixels + double scaled_spacing_v = grid->lyw; // vertical // convert screen pixels to px // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary if (SP_ACTIVE_DESKTOP) { - scaled_spacing /= SP_ACTIVE_DESKTOP->current_zoom(); - } - - NR::Coord rounded; - rounded = Inkscape::Util::round_to_nearest_multiple_plus(p[0], scaled_spacing, grid->origin[0]); - s.push_back(std::make_pair(NR::Dim2(0), rounded)); - rounded = Inkscape::Util::round_to_lower_multiple_plus(p[0], scaled_spacing, grid->origin[0]); - s.push_back(std::make_pair(NR::Dim2(0), rounded)); - + scaled_spacing_h /= SP_ACTIVE_DESKTOP->current_zoom(); + scaled_spacing_v /= SP_ACTIVE_DESKTOP->current_zoom(); + } + + // In an axonometric grid, any point will be surrounded by 6 grid lines: + // - 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 + NR::Coord x_max = Inkscape::Util::round_to_upper_multiple_plus(p[NR::X], scaled_spacing_h, grid->origin[NR::X]); + NR::Coord x_min = Inkscape::Util::round_to_lower_multiple_plus(p[NR::X], scaled_spacing_h, grid->origin[NR::X]); + + // Calculate the y coordinate of the intersection of the angled grid lines with the y-axis + double y_proj_along_z = p[NR::Y] - grid->tan_angle[Z]*(p[NR::X] - grid->origin[NR::X]); + double y_proj_along_x = p[NR::Y] + grid->tan_angle[X]*(p[NR::X] - grid->origin[NR::X]); + double y_proj_along_z_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_z, scaled_spacing_v, grid->origin[NR::Y]); + double y_proj_along_z_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_z, scaled_spacing_v, grid->origin[NR::Y]); + double y_proj_along_x_max = Inkscape::Util::round_to_upper_multiple_plus(y_proj_along_x, scaled_spacing_v, grid->origin[NR::Y]); + double y_proj_along_x_min = Inkscape::Util::round_to_lower_multiple_plus(y_proj_along_x, scaled_spacing_v, grid->origin[NR::Y]); + + // Calculate the normal for the angled grid lines + NR::Point norm_x = NR::rot90(NR::Point(1, -grid->tan_angle[X])); + NR::Point norm_z = NR::rot90(NR::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. + // Now go find that intersection! + Geom::Point result; + Geom::IntersectorKind is = line_intersection(norm_x.to_2geom(), norm_x[NR::Y]*y_proj_along_x_max, + norm_z.to_2geom(), norm_z[NR::Y]*y_proj_along_z_max, + result); + + // 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[NR::X] - grid->origin[NR::X]) < result[Geom::X]; + 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, NR::Point(grid->origin[NR::X], y_proj_along_z_max))); + s.push_back(std::make_pair(norm_x, NR::Point(grid->origin[NR::X], y_proj_along_x_min))); + s.push_back(std::make_pair(component_vectors[NR::X], NR::Point(x_max, 0))); + } + + if (use_right_half) { + s.push_back(std::make_pair(norm_z, NR::Point(grid->origin[NR::X], y_proj_along_z_min))); + s.push_back(std::make_pair(norm_x, NR::Point(grid->origin[NR::X], y_proj_along_x_max))); + s.push_back(std::make_pair(component_vectors[NR::X], NR::Point(x_min, 0))); + } + return s; } diff --git a/src/display/canvas-grid.cpp b/src/display/canvas-grid.cpp index db4648ec0..183b299d1 100644 --- a/src/display/canvas-grid.cpp +++ b/src/display/canvas-grid.cpp @@ -886,10 +886,15 @@ CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const } NR::Coord rounded; + NR::Point point_on_line; + rounded = Inkscape::Util::round_to_upper_multiple_plus(p[i], scaled_spacing, grid->origin[i]); - s.push_back(std::make_pair(NR::Dim2(i), rounded)); + point_on_line = i ? NR::Point(0, rounded) : NR::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]); - s.push_back(std::make_pair(NR::Dim2(i), rounded)); + point_on_line = i ? NR::Point(0, rounded) : NR::Point(rounded, 0); + s.push_back(std::make_pair(component_vectors[i], point_on_line)); } return s; diff --git a/src/guide-snapper.cpp b/src/guide-snapper.cpp index a1df5f1cd..0d7dffd00 100644 --- a/src/guide-snapper.cpp +++ b/src/guide-snapper.cpp @@ -32,13 +32,8 @@ Inkscape::GuideSnapper::LineList Inkscape::GuideSnapper::_getSnapLines(NR::Point for (GSList const *l = _named_view->guides; l != NULL; l = l->next) { SPGuide const *g = SP_GUIDE(l->data); - - /* We assume here that guides are horizontal or vertical */ - if (g->normal == component_vectors[NR::X]) { - s.push_back(std::make_pair(NR::X, g->position)); - } else { - s.push_back(std::make_pair(NR::Y, g->position)); - } + NR::Point point_on_line = (g->normal == component_vectors[NR::X]) ? NR::Point(g->position, 0) : NR::Point(0, g->position); + s.push_back(std::make_pair(g->normal, point_on_line)); } return s; diff --git a/src/line-snapper.cpp b/src/line-snapper.cpp index be35ec2db..446ec73e7 100644 --- a/src/line-snapper.cpp +++ b/src/line-snapper.cpp @@ -1,8 +1,22 @@ +/** + * \file line-snapper.cpp + * \brief LineSnapper class. + * + * Authors: + * Diederik van Lierop + * And others... + * + * Copyright (C) 1999-2007 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + #include "libnr/nr-values.h" #include "libnr/nr-point-fns.h" #include <2geom/geom.h> #include "line-snapper.h" #include "snapped-line.cpp" +#include Inkscape::LineSnapper::LineSnapper(SPNamedView const *nv, NR::Coord const d) : Snapper(nv, d) { @@ -13,14 +27,41 @@ void Inkscape::LineSnapper::_doFreeSnap(SnappedConstraints &sc, Inkscape::Snapper::PointType const &t, NR::Point const &p, bool const &f, - std::vector &points_to_snap, + std::vector &points_to_snap, std::list const &it) const { - /* Snap along x (i.e. to vertical lines) */ - _doConstrainedSnap(sc, t, p, f, points_to_snap, component_vectors[NR::X], it); - /* Snap along y (i.e. to horizontal lines) */ - _doConstrainedSnap(sc, t, p, f, points_to_snap, component_vectors[NR::Y], it); + Inkscape::SnappedPoint s = SnappedPoint(p, NR_HUGE); + /* Get the lines that we will try to snap to */ + const LineList lines = _getSnapLines(p); + + // std::cout << "snap point " << p << " to: " << std::endl; + + for (LineList::const_iterator i = lines.begin(); i != lines.end(); i++) { + NR::Point const p1 = i->second; // point at guide/grid line + NR::Point const p2 = p1 + NR::rot90(i->first); // 2nd point at guide/grid line + + // std::cout << " line through " << i->second << " with normal " << i->first; + + g_assert(i->first != NR::Point(0,0)); // otherwise we'll have div. by zero because NR::L2(d2) = 0 + + // p_proj = projection of p on the grid/guide line running from p1 to p2 + // p_proj = p1 + u (p2 - p1) + // calculate u according to "Minimum Distance between a Point and a Line" + // see http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ + NR::Point const d1(p-p1); // delta 1 + NR::Point const d2(p2-p1); // delta 1 + double const u = (d1[NR::X] * d2[NR::X] + d1[NR::Y] * d2[NR::Y]) / (NR::L2(d2) * NR::L2(d2)); + + NR::Point const p_proj(p1 + u*(p2-p1)); + NR::Coord const dist = NR::L2(p_proj - p); + //Store any line that's within snapping range + if (dist < getDistance()) { + _addSnappedLine(sc, p_proj, dist, i->first, i->second); + // std::cout << " -> distance = " << dist; + } + // std::cout << std::endl; + } } void Inkscape::LineSnapper::_doConstrainedSnap(SnappedConstraints &sc, @@ -45,11 +86,13 @@ void Inkscape::LineSnapper::_doConstrainedSnap(SnappedConstraints &sc, NR::Point const point_on_line = c.hasPoint() ? c.getPoint() : p; /* Constant term of the line we're trying to snap along */ - NR::Coord const q = dot(n, point_on_line); + NR::Coord const q0 = dot(n, point_on_line); + /* Constant term of the grid or guide line */ + NR::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.to_2geom(), q, component_vectors[i->first].to_2geom(), i->second, t_2geom); + Geom::IntersectorKind const k = Geom::line_intersection(n.to_2geom(), q0, i->first.to_2geom(), q1, t_2geom); NR::Point t(t_2geom); if (k == Geom::intersects) { @@ -57,8 +100,6 @@ void Inkscape::LineSnapper::_doConstrainedSnap(SnappedConstraints &sc, //Store any line that's within snapping range if (dist < getDistance()) { _addSnappedLine(sc, t, dist, c.getDirection(), t); - //SnappedLine dummy = SnappedLine(t, dist, c.getDirection(), t); - //sc.infinite_lines.push_back(dummy); } } } diff --git a/src/line-snapper.h b/src/line-snapper.h index 8c307da53..6a1ff9016 100644 --- a/src/line-snapper.h +++ b/src/line-snapper.h @@ -22,23 +22,25 @@ public: LineSnapper(SPNamedView const *nv, NR::Coord const d); 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 private: void _doFreeSnap(SnappedConstraints &sc, - Inkscape::Snapper::PointType const &t, - NR::Point const &p, - bool const &first_point, - std::vector &points_to_snap, - std::list const &it) const; + Inkscape::Snapper::PointType const &t, + NR::Point const &p, + bool const &first_point, + std::vector &points_to_snap, + std::list const &it) const; void _doConstrainedSnap(SnappedConstraints &sc, - Inkscape::Snapper::PointType const &t, - NR::Point const &p, - bool const &first_point, - std::vector &points_to_snap, - ConstraintLine const &c, - std::list const &it) const; + Inkscape::Snapper::PointType const &t, + NR::Point const &p, + bool const &first_point, + std::vector &points_to_snap, + ConstraintLine const &c, + std::list const &it) const; /** * \param p Point that we are trying to snap. diff --git a/src/sp-namedview.cpp b/src/sp-namedview.cpp index a9c6bbfd6..86da7a93a 100644 --- a/src/sp-namedview.cpp +++ b/src/sp-namedview.cpp @@ -159,7 +159,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-intersection-grid-guide"); - sp_object_read_attr(object, "inkscape:snap-snap-intersection-line-segments"); + sp_object_read_attr(object, "inkscape: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"); @@ -374,7 +374,7 @@ static void sp_namedview_set(SPObject *object, unsigned int key, const gchar *va 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); + nv->snap_manager.setSnapIntersectionGG(value ? sp_str_to_bool(value) : TRUE); object->requestModified(SP_OBJECT_MODIFIED_FLAG); break; case SP_ATTR_INKSCAPE_SNAP_INTERS_LINESEGM: -- 2.30.2