Code

Snap to axonometric grid lines
authordvlierop2 <dvlierop2@users.sourceforge.net>
Sat, 8 Dec 2007 21:18:49 +0000 (21:18 +0000)
committerdvlierop2 <dvlierop2@users.sourceforge.net>
Sat, 8 Dec 2007 21:18:49 +0000 (21:18 +0000)
src/display/canvas-axonomgrid.cpp
src/display/canvas-grid.cpp
src/guide-snapper.cpp
src/line-snapper.cpp
src/line-snapper.h
src/sp-namedview.cpp

index a74151523a55202f3cd3e60050f007706d18fe2c..e16e1fdf2db7fd11ea7f047602508eec664a5fa7 100644 (file)
 #include "sp-canvas-util.h"
 #include "canvas-axonomgrid.h"
 #include "util/mathfns.h" 
+#include "2geom/geom.h"
 #include "display-forward.h"
 #include <libnr/nr-pixops.h>
 
-
 #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;
 }
 
index db4648ec08032fc7a1340bef2d609e903bb0b278..183b299d17a1b7314126ea926138a81f2d1c655a 100644 (file)
@@ -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;
index a1df5f1cd339aa906888050d793cbbb4e2483c0a..0d7dffd0071472758f35f42a36bfb2862f723696 100644 (file)
@@ -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;
index be35ec2db930b1faaf717ecad13bb2f7d379853e..446ec73e73deab0c57eafa6545f2b9533597f350 100644 (file)
@@ -1,8 +1,22 @@
+/**
+ * \file line-snapper.cpp
+ * \brief LineSnapper class.
+ *
+ * Authors:
+ *   Diederik van Lierop <mail@diedenrezi.nl>
+ *   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 <gtk/gtk.h>
 
 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<NR::Point> &points_to_snap,
+                                                    std::vector<NR::Point> &points_to_snap,
                                                     std::list<SPItem const *> 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);
             }
         }
     }
index 8c307da53be9fdd7ba77dc1387fdaa9967cf0963..6a1ff9016b105d140cb9d62af7213bc5d29fcb27 100644 (file)
@@ -22,23 +22,25 @@ public:
   LineSnapper(SPNamedView const *nv, NR::Coord const d);
 
 protected:
-  typedef std::list<std::pair<NR::Dim2, NR::Coord> > LineList;
+  typedef std::list<std::pair<NR::Point, NR::Point> > 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<NR::Point> &points_to_snap,
-                    std::list<SPItem const *> const &it) const;
+                   Inkscape::Snapper::PointType const &t,
+                   NR::Point const &p,
+                   bool const &first_point,
+                   std::vector<NR::Point> &points_to_snap,
+                   std::list<SPItem const *> const &it) const;
   
   void _doConstrainedSnap(SnappedConstraints &sc,
-                      Inkscape::Snapper::PointType const &t,
-                      NR::Point const &p,
-                      bool const &first_point,
-                    std::vector<NR::Point> &points_to_snap,
-                    ConstraintLine const &c,
-                      std::list<SPItem const *> const &it) const;
+                          Inkscape::Snapper::PointType const &t,
+                          NR::Point const &p,
+                          bool const &first_point,
+                          std::vector<NR::Point> &points_to_snap,
+                          ConstraintLine const &c,
+                          std::list<SPItem const *> const &it) const;
   
   /**
    *  \param p Point that we are trying to snap.
index a9c6bbfd6d449b905a4ccc7caab97c86b90dae56..86da7a93a40f5634bc83fceca8d909ca99ec56ec 100644 (file)
@@ -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: