Code

since SP_VERB_XMPP_CLIENT is commented out in .h, it must be commented out here to...
[inkscape.git] / src / sp-conn-end.cpp
index f529d6d6276c2bc326ebe086a42ce7a25446b40c..0b420a98e09ec62a74ccadb8f9d1c972f4cc19f4 100644 (file)
@@ -1,6 +1,7 @@
 
 #include <cstring>
 #include <string>
+#include <limits>
 
 #include "display/curve.h"
 #include "libnr/nr-matrix-fns.h"
 #include "sp-path.h"
 #include "uri.h"
 #include "document.h"
+#include "sp-item-group.h"
+#include "2geom/path.h"
+#include "2geom/pathvector.h"
+#include "2geom/path-intersection.h"
 
 
-static void change_endpts(SPCurve *const curve, NR::Point const h2endPt[2]);
-static NR::Point calc_bbox_conn_pt(NR::Rect const &bbox, NR::Point const &p);
-static double signed_one(double const x);
+static void change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2]);
 
 SPConnEnd::SPConnEnd(SPObject *const owner) :
     ref(owner),
@@ -35,8 +38,97 @@ get_nearest_common_ancestor(SPObject const *const obj, SPItem const *const objs[
     return anc_sofar;
 }
 
+
+static bool try_get_intersect_point_with_item_recursive(SPCurve *conn_curve, SPItem& item, 
+        const Geom::Matrix& item_transform, const bool at_start, double* intersect_pos, 
+        unsigned *intersect_index) {
+
+    double initial_pos = (at_start) ? 0.0 : std::numeric_limits<double>::max();
+
+    // if this is a group...
+    if (SP_IS_GROUP(&item)) {
+        SPGroup* group = SP_GROUP(&item);
+        
+        // consider all first-order children
+        double child_pos = initial_pos;
+        unsigned child_index;
+        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_curve, *child_item, 
+                    item_transform * child_item->transform, at_start, &child_pos, &child_index);
+            if (fabs(initial_pos - child_pos) > fabs(initial_pos - *intersect_pos)) {
+                // It is further away from the initial point than the current intersection
+                // point (i.e. the "outermost" intersection), so use this one.
+                *intersect_pos = child_pos;
+                *intersect_index = child_index;
+            }
+        }
+        return *intersect_pos != initial_pos;
+    }
+
+    // if this is a shape...
+    if (!SP_IS_SHAPE(&item)) return false;
+
+    // make sure it has an associated curve
+    SPCurve* item_curve = sp_shape_get_curve(SP_SHAPE(&item));
+    if (!item_curve) return false;
+
+    // apply transformations (up to common ancestor)
+    item_curve->transform(item_transform);
+
+    const Geom::PathVector& curve_pv = item_curve->get_pathvector();
+    const Geom::PathVector& conn_pv = conn_curve->get_pathvector();
+    Geom::CrossingSet cross = crossings(conn_pv, curve_pv);
+    // iterate over all Crossings
+    for (Geom::CrossingSet::const_iterator i = cross.begin(); i != cross.end(); i++) {
+        const Geom::Crossings& cr = *i;
+
+        for (Geom::Crossings::const_iterator i = cr.begin(); i != cr.end(); i++) {
+            const Geom::Crossing& cr_pt = *i;
+            if (fabs(initial_pos - cr_pt.ta) > fabs(initial_pos - *intersect_pos)) {
+                // It is further away from the initial point than the current intersection
+                // point (i.e. the "outermost" intersection), so use this one.
+                *intersect_pos = cr_pt.ta;
+                *intersect_index = cr_pt.a;
+            }
+        }
+    }
+
+    item_curve->unref();
+
+    return *intersect_pos != initial_pos;
+}
+
+
+// This function returns the outermost intersection point between the path (a connector)
+// and the item given.  If the item is a group, then the component items are considered.
+// 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 bool at_start, double* intersect_pos, unsigned *intersect_index) {
+    // We start with the intersection point either at the beginning or end of the 
+    // path, depending on whether we are considering the source or target endpoint.
+    *intersect_pos = (at_start) ? 0.0 : std::numeric_limits<double>::max();
+
+    // Copy the curve and apply transformations up to common ancestor.
+    SPCurve* conn_curve = conn.curve->copy();
+    conn_curve->transform(conn_transform);
+
+    // Find the intersection.
+    bool result = try_get_intersect_point_with_item_recursive(conn_curve, item, item_transform, 
+            at_start, intersect_pos, intersect_index);
+    
+    // Free the curve copy.
+    conn_curve->unref();
+
+    return result;
+}
+
+
 static void
-sp_conn_end_move_compensate(NR::Matrix const */*mp*/, SPItem */*moved_item*/,
+sp_conn_end_move_compensate(Geom::Matrix const */*mp*/, SPItem */*moved_item*/,
                             SPPath *const path,
                             bool const updatePathRepr = true)
 {
@@ -50,98 +142,28 @@ sp_conn_end_move_compensate(NR::Matrix const */*mp*/, SPItem */*moved_item*/,
 
     SPItem *h2attItem[2];
     path->connEndPair.getAttachedItems(h2attItem);
-    if ( !h2attItem[0] && !h2attItem[1] ) {
-        if (updatePathRepr) {
-            path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-            path->updateRepr();
-        }
-        return;
-    }
 
     SPItem const *const path_item = SP_ITEM(path);
     SPObject const *const ancestor = get_nearest_common_ancestor(path_item, h2attItem);
-    NR::Matrix const path2anc(i2anc_affine(path_item, ancestor));
-
-    if (h2attItem[0] != NULL && h2attItem[1] != NULL) {
-        /* Initial end-points: centre of attached object. */
-        NR::Point h2endPt_icoordsys[2];
-        NR::Matrix h2i2anc[2];
-        NR::Rect h2bbox_icoordsys[2];
-        NR::Point last_seg_endPt[2] = {
-            *(path->curve->second_point()),
-            *(path->curve->penultimate_point())
-        };
-        for (unsigned h = 0; h < 2; ++h) {
-            boost::optional<NR::Rect> bbox = h2attItem[h]->getBounds(NR::identity());
-            if (!bbox) {
-                if (updatePathRepr) {
-                    path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-                    path->updateRepr();
-                }
-                return;
-            }
-            h2bbox_icoordsys[h] = *bbox;
-            h2i2anc[h] = i2anc_affine(h2attItem[h], ancestor);
-            h2endPt_icoordsys[h] = h2bbox_icoordsys[h].midpoint();
-        }
-
-        // For each attached object, change the corresponding point to be
-        // on the edge of the bbox.
-        NR::Point h2endPt_pcoordsys[2];
-        for (unsigned h = 0; h < 2; ++h) {
-            h2endPt_icoordsys[h] = calc_bbox_conn_pt(h2bbox_icoordsys[h],
-                                         ( last_seg_endPt[h] * h2i2anc[h].inverse() ));
-            h2endPt_pcoordsys[h] = h2endPt_icoordsys[h] * h2i2anc[h] * path2anc.inverse();
-        }
-        change_endpts(path->curve, h2endPt_pcoordsys);
-    } else {
-        // We leave the unattached endpoint where it is, and adjust the
-        // position of the attached endpoint to be on the edge of the bbox.
-        unsigned ind;
-        NR::Point other_endpt;
-        NR::Point last_seg_pt;
-        if (h2attItem[0] != NULL) {
-            other_endpt = *(path->curve->last_point());
-            last_seg_pt = *(path->curve->second_point());
-            ind = 0;
-        }
-        else {
-            other_endpt = *(path->curve->first_point());
-            last_seg_pt = *(path->curve->penultimate_point());
-            ind = 1;
-        }
-        NR::Point h2endPt_icoordsys[2];
-        NR::Matrix h2i2anc;
-
-        NR::Rect otherpt_rect = NR::Rect(other_endpt, other_endpt);
-        NR::Rect h2bbox_icoordsys[2] = { otherpt_rect, otherpt_rect };
-        boost::optional<NR::Rect> bbox = h2attItem[ind]->getBounds(NR::identity());
-        if (!bbox) {
-            if (updatePathRepr) {
-                path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
-                path->updateRepr();
+    Geom::Matrix const path2anc(i2anc_affine(path_item, ancestor));
+
+    Geom::Point endPts[2] = { *(path->curve->first_point()), *(path->curve->last_point()) };
+    for (unsigned h = 0; h < 2; ++h) {
+        if (h2attItem[h]) {
+            // For each attached object, change the corresponding point to be
+            // at the outermost intersection with the object's path.
+            double intersect_pos;
+            unsigned intersect_index;
+            Geom::Matrix h2i2anc = i2anc_affine(h2attItem[h], ancestor);
+            if ( try_get_intersect_point_with_item(*path, *h2attItem[h], h2i2anc, path2anc, 
+                        (h == 0), &intersect_pos, &intersect_index) ) {
+                const Geom::PathVector& curve = path->curve->get_pathvector();
+                endPts[h] = curve[intersect_index].pointAt(intersect_pos);
             }
-            return;
         }
-
-        h2bbox_icoordsys[ind] = *bbox;
-        h2i2anc = i2anc_affine(h2attItem[ind], ancestor);
-        h2endPt_icoordsys[ind] = h2bbox_icoordsys[ind].midpoint();
-
-        h2endPt_icoordsys[!ind] = other_endpt;
-
-        // For the attached object, change the corresponding point to be
-        // on the edge of the bbox.
-        NR::Point h2endPt_pcoordsys[2];
-        h2endPt_icoordsys[ind] = calc_bbox_conn_pt(h2bbox_icoordsys[ind],
-                                                 ( last_seg_pt * h2i2anc.inverse() ));
-        h2endPt_pcoordsys[ind] = h2endPt_icoordsys[ind] * h2i2anc * path2anc.inverse();
-
-        // Leave the other where it is.
-        h2endPt_pcoordsys[!ind] = other_endpt;
-
-        change_endpts(path->curve, h2endPt_pcoordsys);
     }
+    change_endpts(path->curve, endPts);
     if (updatePathRepr) {
         path->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
         path->updateRepr();
@@ -151,7 +173,7 @@ sp_conn_end_move_compensate(NR::Matrix const */*mp*/, SPItem */*moved_item*/,
 // TODO: This triggering of makeInvalidPath could be cleaned up to be
 //       another option passed to move_compensate.
 static void
-sp_conn_end_shape_move_compensate(NR::Matrix const *mp, SPItem *moved_item,
+sp_conn_end_shape_move_compensate(Geom::Matrix const *mp, SPItem *moved_item,
                             SPPath *const path)
 {
     if (path->connEndPair.isAutoRoutingConn()) {
@@ -180,48 +202,9 @@ sp_conn_adjust_path(SPPath *const path)
     sp_conn_end_move_compensate(NULL, NULL, path, updatePathRepr);
 }
 
-static NR::Point
-calc_bbox_conn_pt(NR::Rect const &bbox, NR::Point const &p)
-{
-    using NR::X;
-    using NR::Y;
-    NR::Point const ctr(bbox.midpoint());
-    NR::Point const lengths(bbox.dimensions());
-    if ( ctr == p ) {
-       /* Arbitrarily choose centre of right edge. */
-       return NR::Point(ctr[X] + .5 * lengths[X],
-                        ctr[Y]);
-    }
-    NR::Point const cp( p - ctr );
-    NR::Dim2 const edgeDim = ( ( fabs(lengths[Y] * cp[X]) <
-                                fabs(lengths[X] * cp[Y])  )
-                              ? Y
-                              : X );
-    NR::Dim2 const otherDim = (NR::Dim2) !edgeDim;
-    NR::Point offset;
-    offset[edgeDim] = (signed_one(cp[edgeDim])
-                      * lengths[edgeDim]);
-    offset[otherDim] = (lengths[edgeDim]
-                       * cp[otherDim]
-                       / fabs(cp[edgeDim]));
-    g_assert((offset[otherDim] >= 0) == (cp[otherDim] >= 0));
-#ifndef NDEBUG
-    for (unsigned d = 0; d < 2; ++d) {
-       g_assert(fabs(offset[d]) <= lengths[d] + .125);
-    }
-#endif
-    return ctr + .5 * offset;
-}
-
-static double signed_one(double const x)
-{
-    return (x < 0
-           ? -1.
-           : 1.);
-}
 
 static void
-change_endpts(SPCurve *const curve, NR::Point const h2endPt[2])
+change_endpts(SPCurve *const curve, Geom::Point const h2endPt[2])
 {
 #if 0
     curve->reset();