X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fsp-conn-end.cpp;h=0b420a98e09ec62a74ccadb8f9d1c972f4cc19f4;hb=847e0c47ad97cc7685f4a02ea380b5c469024f1b;hp=7c0d9650615c7cae9045bcb76c0f0cad29952bd6;hpb=b949b7ecee708059c921bb8afdd1c5461026bc66;p=inkscape.git diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp index 7c0d96506..0b420a98e 100644 --- a/src/sp-conn-end.cpp +++ b/src/sp-conn-end.cpp @@ -1,17 +1,22 @@ +#include +#include +#include + #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), @@ -33,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::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::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) { @@ -48,84 +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] = { - h2attItem[0]->getBounds(NR::identity()), - h2attItem[1]->getBounds(NR::identity()) - }; - 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) { - 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; + 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); + } } - 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 }; - h2bbox_icoordsys[ind] = h2attItem[ind]->getBounds(NR::identity()); - - 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(); @@ -135,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()) { @@ -164,55 +202,16 @@ 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 - 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 } @@ -263,7 +262,7 @@ SPConnEnd::setAttacherHref(gchar const *value) } 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);