Code

Connector tool: make connectors avoid the convex hull of shapes.
authorArcadie M. Cracan <acracan@gmail.com>
Sun, 27 Dec 2009 10:21:31 +0000 (12:21 +0200)
committerArcadie M. Cracan <acracan@gmail.com>
Sun, 27 Dec 2009 10:21:31 +0000 (12:21 +0200)
src/conn-avoid-ref.cpp
src/connector-context.cpp
src/sp-conn-end.cpp

index c04ad9e49a4cb2d3a0c122653faf16a986bf721e..b2aa0ce6bc1e9877242de5f3697a7a90048c410f 100644 (file)
@@ -19,6 +19,7 @@
 #include "2geom/line.h"
 #include "2geom/crossing.h"
 #include "2geom/convex-cover.h"
+#include "helper/geom-curves.h"
 #include "svg/stringstream.h"
 #include "conn-avoid-ref.h"
 #include "connection-points.h"
@@ -32,6 +33,7 @@
 #include "desktop.h"
 #include "desktop-handles.h"
 #include "sp-namedview.h"
+#include "sp-item-group.h"
 #include "inkscape.h"
 #include <glibmm/i18n.h>
 
@@ -56,7 +58,7 @@ SPAvoidRef::~SPAvoidRef()
 {
     _transformed_connection.disconnect();
 
-    // If the document is being destroyed then the router instance 
+    // If the document is being destroyed then the router instance
     // and the ShapeRefs will have been destroyed with it.
     const bool routerInstanceExists = (item->document->router != NULL);
 
@@ -103,10 +105,10 @@ void SPAvoidRef::setConnectionPoints(gchar const *value)
            Update the connectors for which
            the endpoint has changed.
         */
-        
+
         gchar ** strarray = g_strsplit(value, "|", 0);
         gchar ** iter = strarray;
-        
+
         while (*iter != NULL) {
             ConnectionPoint cp;
             Inkscape::SVGIStringStream is(*iter);
@@ -167,17 +169,19 @@ void SPAvoidRef::setConnectionPoints(gchar const *value)
     {
         SPPath* path = SP_PATH(i->data);
         SPConnEnd** connEnds = path->connEndPair.getConnEnds();
-        for (int ix=0; ix<2; ++ix)
-            if (connEnds[ix]->type == ConnPointUserDefined)
-                if (updates.find(connEnds[ix]->id) != updates.end())
-                    if (path->connEndPair.isAutoRoutingConn())
+        for (int ix=0; ix<2; ++ix) {
+            if (connEnds[ix]->type == ConnPointUserDefined) {
+                if (updates.find(connEnds[ix]->id) != updates.end()) {
+                    if (path->connEndPair.isAutoRoutingConn()) {
                         path->connEndPair.tellLibavoidNewEndpoints();
-                    else
-                    {
+                    } else {
                     }
-                else
-                    if (deletes.find(connEnds[ix]->id) != deletes.end())
-                        sp_conn_end_detach(path, ix);
+                }
+                else if (deletes.find(connEnds[ix]->id) != deletes.end()) {
+                    sp_conn_end_detach(path, ix);
+                }
+            }
+        }
     }
     g_slist_free(conns);
     // Remove all deleted connection points
@@ -189,7 +193,7 @@ void SPAvoidRef::setConnectionPoints(gchar const *value)
 void SPAvoidRef::setConnectionPointsAttrUndoable(const gchar* value, const gchar* action)
 {
     SPDocument* doc = SP_OBJECT_DOCUMENT(item);
-    
+
     sp_object_setAttribute( SP_OBJECT(item), "inkscape:connection-points", value, 0 );
     item->updateRepr();
     sp_document_ensure_up_to_date(doc);
@@ -229,8 +233,8 @@ void SPAvoidRef::addConnectionPoint(ConnectionPoint &cp)
     }
     else
         ostr<<'|'<<cp;
-        
-    this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Added a new connection point") );
+
+    this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Add a new connection point") );
 }
 
 void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp)
@@ -255,7 +259,7 @@ void SPAvoidRef::updateConnectionPoint(ConnectionPoint &cp)
             else
                 ostr<<'|'<<*to_write;
         }
-        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Moved a connection point") );
+        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Move a connection point") );
     }
 }
 
@@ -263,21 +267,19 @@ void SPAvoidRef::deleteConnectionPoint(ConnectionPoint &cp)
 {
     Inkscape::SVGOStringStream ostr;
     IdConnectionPointMap::iterator cp_pos = connection_points.find( cp.id );
-    if ( cp_pos != connection_points.end() )
-    {
+    if ( cp_pos != connection_points.end() ) {
         bool first = true;
-        for (IdConnectionPointMap::iterator it = connection_points.begin(); it != connection_points.end(); ++it)
-        {
-            if ( it != cp_pos )
-                if ( first )
-                {
+        for (IdConnectionPointMap::iterator it = connection_points.begin(); it != connection_points.end(); ++it) {
+            if ( it != cp_pos ) {
+                if ( first ) {
                     first = false;
                     ostr<<it->second;
-                }
-                else
+                } else {
                     ostr<<'|'<<it->second;
+                }
+            }
         }
-        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Removed a connection point") );
+        this->setConnectionPointsAttrUndoable( ostr.str().c_str(), _("Remove a connection point") );
     }
 }
 
@@ -304,7 +306,7 @@ void SPAvoidRef::handleSettingChange(void)
     setting = new_setting;
 
     Router *router = item->document->router;
-    
+
     _transformed_connection.disconnect();
     if (new_setting) {
         Avoid::Polygon poly = avoid_item_poly(item);
@@ -314,19 +316,19 @@ void SPAvoidRef::handleSettingChange(void)
 
             const char *id = SP_OBJECT_REPR(item)->attribute("id");
             g_assert(id != NULL);
-            
+
             // Get a unique ID for the item.
             GQuark itemID = g_quark_from_string(id);
 
             shapeRef = new Avoid::ShapeRef(router, poly, itemID);
-        
+
             router->addShape(shapeRef);
         }
     }
     else
     {
         g_assert(shapeRef);
-        
+
         router->removeShape(shapeRef);
         delete shapeRef;
         shapeRef = NULL;
@@ -341,7 +343,7 @@ GSList *SPAvoidRef::getAttachedShapes(const unsigned int type)
     Avoid::IntList shapes;
     GQuark shapeId = g_quark_from_string(item->id);
     item->document->router->attachedShapes(shapes, shapeId, type);
-    
+
     Avoid::IntList::iterator finish = shapes.end();
     for (Avoid::IntList::iterator i = shapes.begin(); i != finish; ++i) {
         const gchar *connId = g_quark_to_string(*i);
@@ -365,7 +367,7 @@ GSList *SPAvoidRef::getAttachedConnectors(const unsigned int type)
     Avoid::IntList conns;
     GQuark shapeId = g_quark_from_string(item->id);
     item->document->router->attachedConns(conns, shapeId, type);
-    
+
     Avoid::IntList::iterator finish = conns.end();
     for (Avoid::IntList::iterator i = conns.begin(); i != finish; ++i) {
         const gchar *connId = g_quark_to_string(*i);
@@ -386,6 +388,7 @@ Geom::Point SPAvoidRef::getConnectionPointPos(const int type, const int id)
     g_assert(item);
     Geom::Point pos;
     const Geom::Matrix& transform = sp_item_i2doc_affine(item);
+    // TODO investigate why this was asking for the active desktop:
     SPDesktop *desktop = inkscape_active_desktop();
 
     if ( type == ConnPointDefault )
@@ -419,146 +422,139 @@ bool SPAvoidRef::isValidConnPointId( const int type, const int id )
         else
             return connection_points.find( id ) != connection_points.end();
     }
-    
+
     return true;
 }
 
-static Avoid::Polygon avoid_item_poly(SPItem const *item)
+static std::vector<Geom::Point> approxCurveWithPoints(SPCurve *curve)
 {
-    SPDesktop *desktop = inkscape_active_desktop();
-    g_assert(desktop != NULL);
-
-    // TODO: The right way to do this is to return the convex hull of
-    //       the object, or an approximation in the case of a rounded
-    //       object.  Specific SPItems will need to have a new
-    //       function that returns points for the convex hull.
-    //       For some objects it is enough to feed the snappoints to
-    //       some convex hull code, though not NR::ConvexHull as this
-    //       only keeps the bounding box of the convex hull currently.
-
-    double spacing = desktop->namedview->connector_spacing;
-
-    // [sommer] If item is a shape, use an approximation of its convex hull
+    // The number of segments to use for not straight curves approximation
+    const unsigned NUM_SEGS = 4;
+    
+    const Geom::PathVector& curve_pv = curve->get_pathvector();
+   
+    // The structure to hold the output
+    std::vector<Geom::Point> poly_points;
+
+    // Iterate over all curves, adding the endpoints for linear curves and
+    // sampling the other curves
+    double seg_size = 1.0 / NUM_SEGS;
+    double at;
+    at = 0;
+    Geom::PathVector::const_iterator pit = curve_pv.begin();
+    while (pit != curve_pv.end())
     {
-        // MJW: Disable this for the moment.  It still has some issues.
-        const bool convex_hull_approximation_enabled = false;
-
-        if ( convex_hull_approximation_enabled && SP_IS_SHAPE (item) ) {
-            // The number of points to use for approximation
-            const unsigned NUM_POINTS = 64; 
-
-//             printf("[sommer] is a shape\n");
-            SPCurve* curve = sp_shape_get_curve (SP_SHAPE (item));
-            if (curve) {
-//                 printf("[sommer] is a curve\n");
-
-                // apply all transformations
-                Geom::Matrix itd_mat = sp_item_i2doc_affine(item);
-                curve->transform(itd_mat);
-
-                // iterate over all paths
-                const Geom::PathVector& curve_pv = curve->get_pathvector();
-                std::vector<Geom::Point> hull_points;
-                for (Geom::PathVector::const_iterator i = curve_pv.begin(); i != curve_pv.end(); i++) {
-                    const Geom::Path& curve_pv_path = *i; 
-//                     printf("[sommer] tracing sub-path\n");
-
-                    // FIXME: enlarge path by "desktop->namedview->connector_spacing" (using sp_selected_path_do_offset)?
-
-                    // use appropriate fraction of points for this path (first one gets any remainder)
-                    unsigned num_points = NUM_POINTS / curve_pv.size();
-                    if (i == curve_pv.begin()) num_points += NUM_POINTS - (num_points * curve_pv.size());
-                    printf("[sommer] using %d points for this path\n", num_points);
-
-                    // sample points along the path for approximation of convex hull
-                    for (unsigned n = 0; n < num_points; n++) {
-                        double at = curve_pv_path.size() / static_cast<double>(num_points) * n;                        
-                        Geom::Point pt = curve_pv_path.pointAt(at);
-                        hull_points.push_back(pt);
-                    }
+        Geom::Path::const_iterator cit = pit->begin();
+        while (cit != pit->end())
+            if (dynamic_cast<Geom::CubicBezier const*>(&*cit))
+            {
+                at += seg_size;
+                if (at <= 1.0 )
+                    poly_points.push_back(cit->pointAt(at));
+                else
+                {
+                    at = 0.0;
+                    ++cit;
                 }
+            }
+            else
+            {
+                poly_points.push_back(cit->finalPoint());
+                ++cit;
+            }
+        ++pit;
+    }
+    return poly_points;
+}
 
-                curve->unref();
-
-                // create convex hull from all sampled points
-                Geom::ConvexHull hull(hull_points);
+static std::vector<Geom::Point> approxItemWithPoints(SPItem const *item, const Geom::Matrix& item_transform)
+{
+    // The structure to hold the output
+    std::vector<Geom::Point> poly_points;
 
-                // store expanded convex hull in Avoid::Polygn
-                unsigned n = 0;
-                Avoid::Polygon poly;
-                const Geom::Point& old_pt = *hull.boundary.begin();
+    if (SP_IS_GROUP(item))
+    {
+        SPGroup* group = SP_GROUP(item);
+        // consider all first-order children
+        for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) {
+            SPItem* child_item = SP_ITEM(i->data);
+            std::vector<Geom::Point> child_points = approxItemWithPoints(child_item, item_transform * child_item->transform);
+            poly_points.insert(poly_points.end(), child_points.begin(), child_points.end());
+        }
+    }
+    else if (SP_IS_SHAPE(item))
+    {
+        SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(item));
+        // make sure it has an associated curve
+        if (item_curve)
+        {
+            // apply transformations (up to common ancestor)
+            item_curve->transform(item_transform);
+            std::vector<Geom::Point> curve_points = approxCurveWithPoints(item_curve);
+            poly_points.insert(poly_points.end(), curve_points.begin(), curve_points.end());
+            item_curve->unref();
+        }
+    }
 
-                Geom::Line hull_edge(*hull.boundary.begin(), *(hull.boundary.begin()+1));
-                Geom::Line parallel_hull_edge;
-                parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
-                parallel_hull_edge.versor(hull_edge.versor());
-                Geom::Line bisector = Geom::make_angle_bisector_line( *(hull.boundary.end()), *hull.boundary.begin(),
-                                                                      *(hull.boundary.begin()+1));
-                Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, bisector);
+    return poly_points;
+}
+static Avoid::Polygon avoid_item_poly(SPItem const *item)
+{
+    SPDesktop *desktop = inkscape_active_desktop();
+    g_assert(desktop != NULL);
+    double spacing = desktop->namedview->connector_spacing;
 
-                if (int_pt)
-                {
-                    Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                            (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
-//                     printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]);
-/*                    printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                                (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/
-                    poly.ps.push_back(avoid_pt);
-                }
-                for (std::vector<Geom::Point>::const_iterator i = hull.boundary.begin() + 1; i != hull.boundary.end(); i++, n++) {
-                        const Geom::Point& old_pt = *i;
-                        Geom::Line hull_edge(*i, *(i+1));
-                        Geom::Line parallel_hull_edge;
-                        parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
-                        parallel_hull_edge.versor(hull_edge.versor());
-                        Geom::Line bisector = Geom::make_angle_bisector_line( *(i-1), *i, *(i+1));
-                        Geom::OptCrossing intersect_pt = Geom::intersection(parallel_hull_edge, bisector);
-
-                        if (int_pt)
-                        {
-                            Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                                  (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
-/*                            printf("[sommer] %f, %f\n", old_pt[Geom::X], old_pt[Geom::Y]);
-                            printf("[sommer] %f, %f\n", (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X], 
-                                                        (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);*/
-                            poly.ps.push_back(avoid_pt);
-                        }
-                }
-                
+    Geom::Matrix itd_mat = sp_item_i2doc_affine(item);
+    std::vector<Geom::Point> hull_points;
+    hull_points = approxItemWithPoints(item, itd_mat);
 
-                return poly;
-            }// else printf("[sommer] is no curve\n");
-        }// else printf("[sommer] is no shape\n");
-    }
-    
-    Geom::OptRect rHull = item->getBounds(sp_item_i2doc_affine(item));
-    if (!rHull) {
-        return Avoid::Polygon();
-    }
+    // create convex hull from all sampled points
+    Geom::ConvexHull hull(hull_points);
 
-    // Add a little buffer around the edge of each object.
-    Geom::Rect rExpandedHull = *rHull;
-    rExpandedHull.expandBy(spacing); 
-    Avoid::Polygon poly(4);
+    // enlarge path by "desktop->namedview->connector_spacing"
+    // store expanded convex hull in Avoid::Polygn
+    Avoid::Polygon poly;
 
-    for (size_t n = 0; n < 4; ++n) {
-        Geom::Point hullPoint = rExpandedHull.corner(n);
-        poly.ps[n].x = hullPoint[Geom::X];
-        poly.ps[n].y = hullPoint[Geom::Y];
+    Geom::Line hull_edge(hull[-1], hull[0]);
+    Geom::Line prev_parallel_hull_edge;
+    prev_parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
+    prev_parallel_hull_edge.versor(hull_edge.versor());
+    int hull_size = hull.boundary.size();
+    for (int i = 0; i <= hull_size; ++i)
+    {
+        hull_edge.setBy2Points(hull[i], hull[i+1]);
+        Geom::Line parallel_hull_edge;
+        parallel_hull_edge.origin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
+        parallel_hull_edge.versor(hull_edge.versor());
+        
+        // determine the intersection point
+        
+        Geom::OptCrossing int_pt = Geom::intersection(parallel_hull_edge, prev_parallel_hull_edge);
+        if (int_pt)
+        {
+            Avoid::Point avoid_pt((parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::X],
+                                    (parallel_hull_edge.origin()+parallel_hull_edge.versor()*int_pt->ta)[Geom::Y]);
+            poly.ps.push_back(avoid_pt);
+        }
+        else
+        {
+            // something went wrong...
+            std::cout<<"conn-avoid-ref.cpp: avoid_item_poly: Geom:intersection failed."<<std::endl;
+        }
+        prev_parallel_hull_edge = parallel_hull_edge;
     }
-
     return poly;
 }
 
 
-GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop, 
+GSList *get_avoided_items(GSList *list, SPObject *from, SPDesktop *desktop,
         bool initialised)
 {
     for (SPObject *child = sp_object_first_child(SP_OBJECT(from)) ;
             child != NULL; child = SP_OBJECT_NEXT(child) ) {
         if (SP_IS_ITEM(child) &&
             !desktop->isLayer(SP_ITEM(child)) &&
-            !SP_ITEM(child)->isLocked() && 
+            !SP_ITEM(child)->isLocked() &&
             !desktop->itemIsHidden(SP_ITEM(child)) &&
             (!initialised || SP_ITEM(child)->avoidRef->shapeRef)
             )
@@ -595,7 +591,7 @@ void init_avoided_shape_geometry(SPDesktop *desktop)
     SPDocument *document = sp_desktop_document(desktop);
     bool saved = sp_document_get_undo_sensitive(document);
     sp_document_set_undo_sensitive(document, false);
-    
+
     bool initialised = false;
     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop,
             initialised);
index 228c81d29e0bdc26365ebe694bf4600778f901f3..307d59d1f5f2efad35e2068b78e710424d0b71f3 100644 (file)
@@ -35,7 +35,7 @@
  *     Gobbling away all duplicates after the current can occasionally result
  *     in the path lagging behind the mouse cursor if it is no longer being
  *     dragged.
- *  o  Fix up libavoid's representation after undo actions.  It doesn't see 
+ *  o  Fix up libavoid's representation after undo actions.  It doesn't see
  *     any transform signals and hence doesn't know shapes have moved back to
  *     there earlier positions.
  *  o  Decide whether drawing/editing mode should be an Inkscape preference
  * ----------------------------------------------------------------------------
  *
  * mjwybrow's observations on acracan's Summer of Code connector work:
- * 
+ *
  *  -  GUI comments:
- * 
+ *
  *      -  Buttons for adding and removing user-specified connection
  *     points should probably have "+" and "-" symbols on them so they
  *     are consistent with the similar buttons for the node tool.
- *      -  Controls on the connector tool be should be reordered logically, 
+ *      -  Controls on the connector tool be should be reordered logically,
  *     possibly as follows:
- * 
- *     *Connector*: [Polyline-radio-button] [Orthgonal-radio-button] 
+ *
+ *     *Connector*: [Polyline-radio-button] [Orthgonal-radio-button]
  *       [Curvature-control] | *Shape*: [Avoid-button] [Dont-avoid-button]
  *       [Spacing-control] | *Connection pts*: [Edit-mode] [Add-pt] [Rm-pt]
- * 
- *     I think that the network layout controls be moved to the 
- *     Align and Distribute dialog (there is already the layout button 
+ *
+ *     I think that the network layout controls be moved to the
+ *     Align and Distribute dialog (there is already the layout button
  *     there, but no options are exposed).
- * 
+ *
  *     I think that the style change between polyline and orthogonal
  *     would be much clearer with two buttons (radio behaviour -- just
  *     one is true).
- * 
- *     The other tools show a label change from "New:" to "Change:" 
+ *
+ *     The other tools show a label change from "New:" to "Change:"
  *     depending on whether an object is selected.  We could consider
  *     this but there may not be space.
- * 
+ *
  *     The Add-pt and Rm-pt buttons should be greyed out (inactive) if
  *     we are not in connection point editing mode.  And probably also
  *     if there is no shape selected, i.e. at the times they have no
  *     effect when clicked.
- * 
- *     Likewise for the avoid/ignore shapes buttons.  These should be 
+ *
+ *     Likewise for the avoid/ignore shapes buttons.  These should be
  *     inactive when a shape is not selected in the connector context.
- * 
+ *
  *  -  When creating/editing connection points:
- * 
+ *
  *      -  Strange things can happen if you have connectors selected, or
- *     try rerouting connectors by dragging their endpoints when in 
+ *     try rerouting connectors by dragging their endpoints when in
  *     connection point editing mode.
- * 
+ *
  *      -  Possibly the selected shape's connection points should always
  *     be shown (i.e., have knots) when in editing mode.
- * 
+ *
  *      -  It is a little strange to be able to place connection points
  *     competely outside shapes.  Especially when you later can't draw
  *     connectors to them since the knots are only visible when you
  *     are over the shape.  I think that you should only be able to
- *     place connection points inside or on the boundary of the shape 
+ *     place connection points inside or on the boundary of the shape
  *     itself.
- *  
- *      -  The intended ability to place a new point at the current cursor 
+ *
+ *      -  The intended ability to place a new point at the current cursor
  *     position by pressing RETURN does not seem to work.
- * 
+ *
  *      -  The Status bar tooltip should change to reflect editing mode
  *     and tell the user about RETURN and how to use the tool.
- * 
+ *
  *  -  Connection points general:
- * 
- *      -  Connection points that were inside the shape can end up outside 
+ *
+ *      -  Connection points that were inside the shape can end up outside
  *     after a rotation is applied to the shape in the select tool.
  *     It doesn't seem like the correct transform is being applied to
  *     these, or it is being applied at the wrong time.  I'd expect
  *     connection points to rotate with the shape, and stay at the
  *     same position "on the shape"
- * 
+ *
  *      -  I was able to make the connectors attached to a shape fall off
  *     the shape after scaling it.  Not sure the exact cause, but may
  *     require more investigation/debugging.
- * 
+ *
  *      -  The user-defined connection points should be either absolute
  *     (as the current ones are) or defined as a percentage of the
  *     shape.  These would be based on a toggle setting on the
  *     toolbar, and they would be placed in exactly the same way by
  *     the user.  The only difference would be that they would be
  *     store as percentage positions in the SVG connection-points
- *     property and that they would update/move automatically if the 
+ *     property and that they would update/move automatically if the
  *     object was resized or scaled.
- * 
+ *
  *      -  Thinking more, I think you always want to store and think about
  *     the positions of connection points to be pre-transform, but
  *     obviously the shape transform is applied to them.  That way,
  *     the shape transform is altered.  The Percentage version would
  *     compute their position from the pre-transform dimensions and
  *     then have the transform applied to them, for example.
- * 
+ *
  *      -  The connection points in the test_connection_points.svg file
  *     seem to follow the shape when it is moved, but connection
  *     points I add to new shapes, do not follow the shape, either
  *     when the shape is just moved or transformed.  There is
- *     something wrong here.  What exactly should the behaviour be 
+ *     something wrong here.  What exactly should the behaviour be
  *     currently?
- * 
+ *
  *      -  I see that connection points are specified at absolute canvas
  *     positions.  I really think that they should be specified in
  *     shape coordinated relative to the shapes.  There may be
@@ -333,7 +333,7 @@ sp_connector_context_init(SPConnectorContext *cc)
     cc->clickedhandle = NULL;
 
     new (&cc->connpthandles) ConnectionPointMap();
-    
+
     for (int i = 0; i < 2; ++i) {
         cc->endpt_handle[i] = NULL;
         cc->endpt_handler_id[i] = 0;
@@ -353,7 +353,7 @@ sp_connector_context_dispose(GObject *object)
     cc->sel_changed_connection.disconnect();
 
     if (!cc->connpthandles.empty()) {
-        for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); 
+        for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
                 it != cc->connpthandles.end(); ++it) {
             g_object_unref(it->first);
         }
@@ -413,7 +413,7 @@ sp_connector_context_setup(SPEventContext *ec)
     cc_selection_changed(cc->selection, (gpointer) cc);
 
     cc->within_tolerance = false;
-    
+
     sp_event_context_read(ec, "curvature");
     sp_event_context_read(ec, "orthogonal");
     sp_event_context_read(ec, "mode");
@@ -472,7 +472,7 @@ void sp_connector_context_switch_mode(SPEventContext* ec, unsigned int newMode)
         {
             cc->selection->set( SP_OBJECT( cc->active_shape ) );
         }
-        else 
+        else
         {
             SPItem* item = cc->selection->singleItem();
             if ( item )
@@ -537,7 +537,7 @@ cc_clear_active_shape(SPConnectorContext *cc)
 
     // Hide the connection points if they exist.
     if (cc->connpthandles.size()) {
-        for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); 
+        for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
                 it != cc->connpthandles.end(); ++it) {
             sp_knot_hide(it->first);
         }
@@ -616,7 +616,7 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G
     SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context);
 
     Geom::Point p(event->button.x, event->button.y);
-    
+
     switch (event->type) {
         case GDK_BUTTON_RELEASE:
             if (event->button.button == 1 && !event_context->space_panning) {
@@ -660,11 +660,11 @@ sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, G
             if (cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE || (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && !cc->selected_handle))
             {
                 if (cc_item_is_shape(item)) {
-                    
+
                     // I don't really understand what the above does,
                     // so I commented it.
                     // This is a shape, so show connection point(s).
-    /*                if (!(cc->active_shape) 
+    /*                if (!(cc->active_shape)
                             // Don't show handle for another handle.
     //                         || (cc->connpthandles.find((SPKnot*) item) != cc->connpthandles.end())
                         )
@@ -847,7 +847,7 @@ connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const
             cc->xp = bevent.x;
             cc->yp = bevent.y;
             cc->within_tolerance = true;
-            
+
             ConnectionPointMap::iterator const& active_knot_it = cc->connpthandles.find( cc->active_handle );
 
             switch (cc->state)
@@ -934,7 +934,7 @@ connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion cons
     {
         SnapManager &m = dt->namedview->snap_manager;
         m.setup(dt);
-        
+
         switch (cc->state) {
             case SP_CONNECTOR_CONTEXT_DRAGGING:
             {
@@ -1074,7 +1074,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
             switch ( cc->state )
             {
                 case SP_CONNECTOR_CONTEXT_DRAGGING:
-                    
+
                     if (!cc->within_tolerance)
                     {
 //                        sp_event_context_snap_window_open(event_context);
@@ -1094,7 +1094,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
                 case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
 //                    sp_event_context_snap_window_open( event_context );
                     m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
-                    
+
                     sp_knot_set_position(cc->selected_handle, p, 0);
 //                    sp_event_context_snap_window_closed(event_context);
 
@@ -1118,7 +1118,7 @@ connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton con
             }
         }
     }
-    
+
 
     return ret;
 }
@@ -1196,8 +1196,8 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                     SnapManager &m = desktop->namedview->snap_manager;
                     m.setup(desktop);
                     Geom::Point p = cc->selected_handle->pos;
-                    SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
-                    
+//                     SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
+
                     if (!cc->within_tolerance)
                     {
 //                        sp_event_context_snap_window_open(event_context);
@@ -1229,11 +1229,11 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                     SnapManager &m = desktop->namedview->snap_manager;
                     m.setup(desktop);
                     Geom::Point p = cc->selected_handle->pos;
-                    SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
-                    
+//                     SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
+
 //                    sp_event_context_snap_window_open( event_context );
                     m.freeSnapReturnByRef(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::SNAPSOURCE_HANDLE);
-                    
+
                     sp_knot_set_position(cc->selected_handle, p, 0);
 //                    sp_event_context_snap_window_closed(event_context);
 
@@ -1263,7 +1263,7 @@ connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
                     cc->selected_handle = NULL;
                     ret = TRUE;
                 }
-                
+
                 break;
         }
     }
@@ -1453,7 +1453,7 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
         // Process pending updates.
         cc->newconn->updateRepr();
         sp_document_ensure_up_to_date(doc);
-        
+
         if (connection) {
             // Adjust endpoints to shape edge.
             sp_conn_reroute_path_immediate(SP_PATH(cc->newconn));
@@ -1461,7 +1461,7 @@ spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
         }
 
         // Only set the selection after we are finished with creating the attributes of
-        // the connector.  Otherwise, the selection change may alter the defaults for 
+        // the connector.  Otherwise, the selection change may alter the defaults for
         // values like curvature in the connector context, preventing subsequent lookup
         // of their original values.
         cc->selection->set(repr);
@@ -1630,7 +1630,7 @@ static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, Connectio
 static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
 {
     g_assert(item != NULL );
-    
+
     std::map<int, ConnectionPoint>* connpts = &item->avoidRef->connection_points;
 
     if (cc->active_shape != item)
@@ -1671,7 +1671,7 @@ static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
         if ( connpts->size() )
         for (std::map<int, ConnectionPoint>::iterator it = connpts->begin(); it != connpts->end(); ++it)
             cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second);
-        
+
         // Also add default connection points
         // For now, only centre default connection point will
         // be available
index 52914ee9e0bab7b7f770a40820526ef07c9c53b4..224442eb852370a877a160070d957f0ad09e6b13 100644 (file)
@@ -46,14 +46,14 @@ static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_p
     // if this is a group...
     if (SP_IS_GROUP(item)) {
         SPGroup* group = SP_GROUP(item);
-        
+
         // consider all first-order children
-        double child_pos = std::numeric_limits<double>::max();
+        double child_pos = 0.0;
         for (GSList const* i = sp_item_group_item_list(group); i != NULL; i = i->next) {
             SPItem* child_item = SP_ITEM(i->data);
             try_get_intersect_point_with_item_recursive(conn_pv, child_item,
                     item_transform * child_item->transform, child_pos);
-            if (intersect_pos > child_pos)
+            if (intersect_pos < child_pos)
                 intersect_pos = child_pos;
         }
         return intersect_pos != initial_pos;
@@ -77,7 +77,7 @@ static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_p
 
         for (Geom::Crossings::const_iterator i = cr.begin(); i != cr.end(); i++) {
             const Geom::Crossing& cr_pt = *i;
-            if ( intersect_pos > cr_pt.ta)
+            if ( intersect_pos < cr_pt.ta)
                 intersect_pos = cr_pt.ta;
         }
     }
@@ -93,9 +93,9 @@ static bool try_get_intersect_point_with_item_recursive(Geom::PathVector& conn_p
 // The transforms given should be to a common ancestor of both the path and item.
 //
 static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
-        const Geom::Matrix& item_transform, const Geom::Matrix& conn_transform, 
+        const Geom::Matrix& item_transform, const Geom::Matrix& conn_transform,
         const bool at_start, double& intersect_pos) {
+
     // Copy the curve and apply transformations up to common ancestor.
     SPCurve* conn_curve = conn->curve->copy();
     conn_curve->transform(conn_transform);
@@ -109,8 +109,8 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
         conn_pv[0] = conn_pv[0].reverse();
     }
 
-    // We start with the intersection point at the end of the path
-    intersect_pos = conn_pv[0].size();
+    // We start with the intersection point at the beginning of the path
+    intersect_pos = 0.0;
 
     // Find the intersection.
     bool result = try_get_intersect_point_with_item_recursive(conn_pv, item, item_transform, intersect_pos);
@@ -130,7 +130,7 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
 
 
 static void
-sp_conn_get_route_and_redraw(SPPath *const path, 
+sp_conn_get_route_and_redraw(SPPath *const path,
         const bool updatePathRepr = true)
 {
     // Get the new route around obstacles.
@@ -138,7 +138,7 @@ sp_conn_get_route_and_redraw(SPPath *const path,
     if (!rerouted) {
         return;
     }
-    
+
     SPItem *h2attItem[2];
     path->connEndPair.getAttachedItems(h2attItem);
 
@@ -146,7 +146,7 @@ sp_conn_get_route_and_redraw(SPPath *const path,
     SPObject const *const ancestor = get_nearest_common_ancestor(path_item, h2attItem);
     Geom::Matrix const path2anc(i2anc_affine(path_item, ancestor));
 
-    // Set sensible values incase there the connector ends are not 
+    // Set sensible values incase there the connector ends are not
     // attached to any shapes.
     Geom::PathVector conn_pv = path->curve->get_pathvector();
     double endPos[2] = { 0, conn_pv[0].size() };
@@ -241,7 +241,7 @@ sp_conn_end_detach(SPObject *const owner, unsigned const handle_ix)
 }
 
 void
-SPConnEnd::setAttacherHref(gchar const *value, SPPath* path)
+SPConnEnd::setAttacherHref(gchar const *value, SPPath* /*path*/)
 {
     if ( value && href && ( strcmp(value, href) == 0 ) ) {
         /* No change, do nothing. */
@@ -293,7 +293,7 @@ SPConnEnd::setAttacherHref(gchar const *value, SPPath* path)
                 }
             }
             // Check to see if the connection point changed and update it.
-            // 
+            //
 
             if ( !value_strarray[1] )
             {
@@ -353,12 +353,12 @@ SPConnEnd::setAttacherHref(gchar const *value, SPPath* path)
                     validRef = false;
                 }
             }
-            
+
             if ( changed )
             {
                 // We still have to verify that the reference to the
                 // connection point is a valid one.
-                
+
                 // Get the item the connector is attached to
                 SPItem* item = ref.getObject();
                 if ( item && !item->avoidRef->isValidConnPointId( type, id ) )
@@ -370,7 +370,8 @@ SPConnEnd::setAttacherHref(gchar const *value, SPPath* path)
                     // Update the connector
                     if (path->connEndPair.isAutoRoutingConn()) {
                         path->connEndPair.tellLibavoidNewEndpoints();
-                    }*/
+                    }
+*/
             }
 
             if ( !validRef )