X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fconn-avoid-ref.cpp;h=b2aa0ce6bc1e9877242de5f3697a7a90048c410f;hb=eb4f7092b0b8482c042370d8c6520d4b05997afe;hp=c04ad9e49a4cb2d3a0c122653faf16a986bf721e;hpb=42b53baed61b6b06f33ecfa440a747382eb350df;p=inkscape.git diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp index c04ad9e49..b2aa0ce6b 100644 --- a/src/conn-avoid-ref.cpp +++ b/src/conn-avoid-ref.cpp @@ -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 @@ -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<<'|'<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<second; - } - else + } else { ostr<<'|'<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 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 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 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(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(&*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 approxItemWithPoints(SPItem const *item, const Geom::Matrix& item_transform) +{ + // The structure to hold the output + std::vector 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 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 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::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 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."<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);