diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp
index 7a8a60dd088dde3f5fdb94c16f48b1b3db13905b..0b420a98e09ec62a74ccadb8f9d1c972f4cc19f4 100644 (file)
--- a/src/sp-conn-end.cpp
+++ b/src/sp-conn-end.cpp
+#include <cstring>
+#include <string>
+#include <limits>
+
#include "display/curve.h"
-#include "libnr/nr-matrix-div.h"
#include "libnr/nr-matrix-fns.h"
#include "xml/repr.h"
#include "sp-conn-end.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),
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)
{
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] = {
- sp_curve_second_point(path->curve),
- sp_curve_penultimate_point(path->curve)
- };
- for (unsigned h = 0; h < 2; ++h) {
- NR::Maybe<NR::Rect> bbox = h2attItem[h]->getBounds(NR::identity());
- if (bbox) {
- h2bbox_icoordsys[h] = *bbox;
- } else {
- // FIXME
- h2bbox_icoordsys[h] = NR::Rect(NR::Point(0, 0), NR::Point(0, 0));
+ 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);
}
- 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] ));
- h2endPt_pcoordsys[h] = h2endPt_icoordsys[h] * h2i2anc[h] / path2anc;
- }
- 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 = sp_curve_last_point(path->curve);
- last_seg_pt = sp_curve_second_point(path->curve);
- ind = 0;
- }
- else {
- other_endpt = sp_curve_first_point(path->curve);
- last_seg_pt = sp_curve_penultimate_point(path->curve);
- 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 };
- NR::Maybe<NR::Rect> bbox = h2attItem[ind]->getBounds(NR::identity());
- if (bbox) {
- h2bbox_icoordsys[ind] = *bbox;
- } else {
- // FIXME
- h2bbox_icoordsys[ind] = NR::Rect(NR::Point(0, 0), NR::Point(0, 0));
- }
-
- 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 ));
- h2endPt_pcoordsys[ind] = h2endPt_icoordsys[ind] * h2i2anc / path2anc;
-
- // 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();
// 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()) {
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
- sp_curve_reset(curve);
- sp_curve_moveto(curve, h2endPt[0]);
- sp_curve_lineto(curve, h2endPt[1]);
+ curve->reset();
+ curve->moveto(h2endPt[0]);
+ curve->lineto(h2endPt[1]);
#else
- sp_curve_move_endpoints(curve, h2endPt[0], h2endPt[1]);
+ curve->move_endpoints(h2endPt[0], h2endPt[1]);
#endif
}
}
void
-sp_conn_end_href_changed(SPObject *old_ref, SPObject *ref,
+sp_conn_end_href_changed(SPObject */*old_ref*/, SPObject */*ref*/,
SPConnEnd *connEndPtr, SPPath *const path, unsigned const handle_ix)
{
g_return_if_fail(connEndPtr != NULL);