// SPDX-License-Identifier: GPL-2.0-or-later /** * @file * Interface between Inkscape code (SPItem) and graphlayout functions. */ /* * Authors: * Tim Dwyer * Abhishek Sharma * * Copyright (C) 2005 Authors * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include #include #include #include #include #include #include #include #include <2geom/transforms.h> #include "conn-avoid-ref.h" #include "desktop.h" #include "graphlayout.h" #include "inkscape.h" #include "3rdparty/adaptagrams/libavoid/router.h" #include "3rdparty/adaptagrams/libcola/cola.h" #include "3rdparty/adaptagrams/libcola/connected_components.h" #include "object/sp-item-transform.h" #include "object/sp-namedview.h" #include "object/sp-path.h" #include "style.h" using namespace cola; using namespace vpsc; /** * Returns true if item is a connector */ bool isConnector(SPItem const * const item) { auto path = dynamic_cast(item); return path && path->connEndPair.isAutoRoutingConn(); } struct CheckProgress: TestConvergence { CheckProgress(double d, unsigned i, std::list & selected, Rectangles & rs, std::map & nodelookup) : TestConvergence(d, i) , selected(selected) , rs(rs) , nodelookup(nodelookup) {} bool operator()(const double new_stress, std::valarray & X, std::valarray & Y) override { /* This is where, if we wanted to animate the layout, we would need to update * the positions of all objects and redraw the canvas and maybe sleep a bit cout << "stress="<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 (SPItem* conn: connectors) { SPPath* path = SP_PATH(conn); std::array attachedItems; path->connEndPair.getAttachedItems(attachedItems.data()); if (attachedItems[0] == nullptr) continue; if (attachedItems[1] == nullptr) continue; std::map::iterator i_iter=nodelookup.find(attachedItems[0]->getId()); if (i_iter == nodelookup.end()) continue; unsigned rect_index_first = i_iter->second; i_iter = nodelookup.find(attachedItems[1]->getId()); if (i_iter == nodelookup.end()) continue; unsigned rect_index_second = i_iter->second; es.emplace_back(rect_index_first, rect_index_second); if (conn->style->marker_end.set) { if (directed && strcmp(conn->style->marker_end.value(), "none")) { constraints.push_back(new SeparationConstraint(YDIM, rect_index_first, rect_index_second, ideal_connector_length * directed_edge_height_modifier)); } } } EdgeLengths elengths(es.size(), 1); std::vector cs; connectedComponents(rs, es, cs); for (Component * c: cs) { if (c->edges.size() < 2) continue; CheckProgress test(0.0001, 100, selected, rs, nodelookup); ConstrainedMajorizationLayout alg(c->rects, c->edges, nullptr, ideal_connector_length, elengths, &test); if (avoid_overlaps) alg.setAvoidOverlaps(); alg.setConstraints(&constraints); alg.run(); } separateComponents(cs); for (SPItem * item: selected) { if (!isConnector(item)) { std::map::iterator i = nodelookup.find(item->getId()); if (i != nodelookup.end()) { Rectangle * r = rs[i->second]; Geom::OptRect item_box = item->desktopVisualBounds(); if (item_box) { Geom::Point const curr(item_box->midpoint()); Geom::Point const dest(r->getCentreX(),r->getCentreY()); item->move_rel(Geom::Translate(dest - curr)); } } } } for (CompoundConstraint * c: constraints) { delete c; } for (Rectangle * r: rs) { delete r; } } // vim: set cindent // vim: ts=4 sw=4 et tw=0 wm=0 /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :