static void sp_canvas_bpath_update(SPCanvasItem *item, Geom::Affine const &affine, unsigned int flags) { SPCanvasBPath *cbp = SP_CANVAS_BPATH(item); item->canvas->requestRedraw((int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2); if (reinterpret_cast<SPCanvasItemClass *>(sp_canvas_bpath_parent_class)->update) { reinterpret_cast<SPCanvasItemClass *>(sp_canvas_bpath_parent_class)->update(item, affine, flags); } sp_canvas_item_reset_bounds (item); if (!cbp->curve) return; cbp->affine = affine; Geom::OptRect bbox = bounds_exact_transformed(cbp->curve->get_pathvector(), affine); if (bbox) { item->x1 = (int)bbox->min()[Geom::X] - 1; item->y1 = (int)bbox->min()[Geom::Y] - 1; item->x2 = (int)bbox->max()[Geom::X] + 1; item->y2 = (int)bbox->max()[Geom::Y] + 1; } else { item->x1 = 0; item->y1 = 0; item->x2 = 0; item->y2 = 0; } item->canvas->requestRedraw((int)item->x1, (int)item->y1, (int)item->x2, (int)item->y2); }
/** * Takes a list of inkscape items, extracts the graph defined by * connectors between them, and uses graph layout techniques to find * a nice layout */ void graphlayout(std::vector<SPItem*> const &items) { if(items.empty()) { return; } list<SPItem *> selected; filterConnectors(items,selected); if (selected.empty()) return; const unsigned n=selected.size(); //Check 2 or more selected objects if (n < 2) return; // add the connector spacing to the size of node bounding boxes // so that connectors can always be routed between shapes SPDesktop* desktop = SP_ACTIVE_DESKTOP; double spacing = 0; if(desktop) spacing = desktop->namedview->connector_spacing+0.1; map<string,unsigned> nodelookup; vector<Rectangle*> rs; vector<Edge> es; for (list<SPItem *>::iterator i(selected.begin()); i != selected.end(); ++i) { SPItem *u=*i; Geom::OptRect const item_box = u->desktopVisualBounds(); if(item_box) { Geom::Point ll(item_box->min()); Geom::Point ur(item_box->max()); nodelookup[u->getId()]=rs.size(); rs.push_back(new Rectangle(ll[0]-spacing,ur[0]+spacing, ll[1]-spacing,ur[1]+spacing)); } else { // I'm not actually sure if it's possible for something with a // NULL item-box to be attached to a connector in which case we // should never get to here... but if such a null box can occur it's // probably pretty safe to simply ignore //fprintf(stderr,"NULL item_box found in graphlayout, ignoring!\n"); } } Inkscape::Preferences *prefs = Inkscape::Preferences::get(); SimpleConstraints scx,scy; double ideal_connector_length = prefs->getDouble("/tools/connector/length", 100.0); double directed_edge_height_modifier = 1.0; bool directed = prefs->getBool("/tools/connector/directedlayout"); bool avoid_overlaps = prefs->getBool("/tools/connector/avoidoverlaplayout"); for (list<SPItem *>::iterator i(selected.begin()); i != selected.end(); ++i) { SPItem *iu=*i; map<string,unsigned>::iterator i_iter=nodelookup.find(iu->getId()); map<string,unsigned>::iterator i_iter_end=nodelookup.end(); if(i_iter==i_iter_end) { continue; } unsigned u=i_iter->second; std::vector<SPItem *> nlist=iu->avoidRef->getAttachedConnectors(Avoid::runningFrom); list<SPItem *> connectors; connectors.insert(connectors.end(), nlist.begin(), nlist.end()); for (list<SPItem *>::iterator j(connectors.begin()); j != connectors.end(); ++j) { SPItem *conn=*j; SPItem *iv; SPItem *items[2]; assert(isConnector(conn)); SP_PATH(conn)->connEndPair.getAttachedItems(items); if(items[0]==iu) { iv=items[1]; } else { iv=items[0]; } if (iv == NULL) { // The connector is not attached to anything at the // other end so we should just ignore it. continue; } // If iv not in nodelookup we again treat the connector // as disconnected and continue map<string,unsigned>::iterator v_pair=nodelookup.find(iv->getId()); if(v_pair!=nodelookup.end()) { unsigned v=v_pair->second; //cout << "Edge: (" << u <<","<<v<<")"<<endl; es.push_back(make_pair(u,v)); if(conn->style->marker_end.set) { if(directed && strcmp(conn->style->marker_end.value,"none")) { scy.push_back(new SimpleConstraint(v, u, (ideal_connector_length * directed_edge_height_modifier))); } } } } } const unsigned E = es.size(); double eweights[E]; fill(eweights,eweights+E,1); vector<Component*> cs; connectedComponents(rs,es,scx,scy,cs); for(unsigned i=0;i<cs.size();i++) { Component* c=cs[i]; if(c->edges.size()<2) continue; CheckProgress test(0.0001,100,selected,rs,nodelookup); ConstrainedMajorizationLayout alg(c->rects,c->edges,eweights,ideal_connector_length,test); alg.setupConstraints(NULL,NULL,avoid_overlaps, NULL,NULL,&c->scx,&c->scy,NULL,NULL); alg.run(); } separateComponents(cs); for (list<SPItem *>::iterator it(selected.begin()); it != selected.end(); ++it) { SPItem *u=*it; if(!isConnector(u)) { map<string,unsigned>::iterator i=nodelookup.find(u->getId()); if(i!=nodelookup.end()) { Rectangle* r=rs[i->second]; Geom::OptRect item_box = u->desktopVisualBounds(); if (item_box) { Geom::Point const curr(item_box->midpoint()); Geom::Point const dest(r->getCentreX(),r->getCentreY()); sp_item_move_rel(u, Geom::Translate(dest - curr)); } } } } for(unsigned i=0;i<scx.size();i++) { delete scx[i]; } for(unsigned i=0;i<scy.size();i++) { delete scy[i]; } for(unsigned i=0;i<rs.size();i++) { delete rs[i]; } }