From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/3rdparty/adaptagrams/libcola/cola.h | 1002 +++++++++++++++++++++++++++++++ 1 file changed, 1002 insertions(+) create mode 100644 src/3rdparty/adaptagrams/libcola/cola.h (limited to 'src/3rdparty/adaptagrams/libcola/cola.h') diff --git a/src/3rdparty/adaptagrams/libcola/cola.h b/src/3rdparty/adaptagrams/libcola/cola.h new file mode 100644 index 0000000..40043cd --- /dev/null +++ b/src/3rdparty/adaptagrams/libcola/cola.h @@ -0,0 +1,1002 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libcola - A library providing force-directed network layout using the + * stress-majorization method subject to separation constraints. + * + * Copyright (C) 2006-2015 Monash University + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Tim Dwyer + * Michael Wybrow +*/ + +#ifndef COLA_H +#define COLA_H + +#include +#include +#include +#include +#include +#include +#include + +#include "libcola/gradient_projection.h" +#include "libcola/cluster.h" +#include "libcola/straightener.h" +#include "libcola/exceptions.h" +#include "libcola/pseudorandom.h" + +namespace vpsc { class Rectangle; } +namespace topology { + class ColaTopologyAddon; +} +namespace dialect { + class Graph; +} + + +/** + * @namespace cola + * @brief libcola: Force-directed network layout subject to + * separation constraints library. + * + * You should use COLA via an instance of the ConstrainedFDLayout class. +*/ +namespace cola { + +class NonOverlapConstraints; +class NonOverlapConstraintExemptions; + +//! @brief A vector of node Indexes. +typedef std::vector NodeIndexes; + +//! @brief A vector of NodeIndexes. +typedef std::vector ListOfNodeIndexes; + +//! Edges are simply a pair of indices to entries in the Node vector +typedef std::pair Edge; + +//! EdgeLengths is a vector of ideal lengths for edges corresponding to +//! edges in the edge list. +typedef std::vector EdgeLengths; +#define StandardEdgeLengths EdgeLengths() + +/** + * @brief A Lock specifies a required position for a node. + */ +class Lock { +public: + Lock() {} + /** + * @brief Constructs a Lock object for a given node and position. + * + * @param[in] id The index of the node in the Rectangles vector. + * @param[in] X The node's fixed position in the x-dimension. + * @param[in] Y The node's fixed position in the y-dimension. + */ + Lock(unsigned id, double X, double Y) : id(id), x(X), y(Y) { + } + unsigned getID() const { + return id; + } + double pos(vpsc::Dim dim) const { + return dim==vpsc::HORIZONTAL?x:y; + } +private: + unsigned id; + double x; + double y; +}; +//! @brief A vector of Lock objects. +typedef std::vector Locks; + +/** + * @brief A Resize specifies a new required bounding box for a node. + */ +class Resize { +public: + Resize() {} + /** + * @brief Constructs a Resize object for a given node and it's new + * bounding box. + * + * @param[in] id The index of the node in the Rectangles vector. + * @param[in] x The minimum horizontal value for the node's new + * bounding box. + * @param[in] y The minimum vertical value for the node's new + * bounding box. + * @param[in] w The width value for the node's new bounding box. + * @param[in] h The height value for the node's new bounding box. + */ + Resize(unsigned id, double x, double y, double w, double h) + : id(id), target(x,x+w,y,y+h) {} + unsigned getID() const { + return id; + } + const vpsc::Rectangle* getTarget() const { + return ⌖ + } +private: + unsigned id; + vpsc::Rectangle target; +}; +//! @brief A vector of Resize objects. +typedef std::vector Resizes; + +/* + * Setting a desired position for a node adds a term to the goal function + * drawing the node towards that desired position + */ +struct DesiredPosition { + unsigned id; + double x; + double y; + double weight; +}; +typedef std::vector DesiredPositions; + +/* + * desired positions which should override those computed by applying forces + * are passed in for a set of nodes. The first entry is the Node->id, the + * second is the desired position. + */ +typedef std::pair DesiredPositionInDim; +typedef std::vector DesiredPositionsInDim; + +/** + * @brief A default functor that is called before each iteration in the + * main loop of the ConstrainedFDLayout::run() method. + * + * Override the operator() for things like locking the position of nodes + * for the duration of the iteration. + * + * If the operator() returns false the subsequent iterations are + * abandoned, i.e., layout ends immediately. You can make it return true + * when a user-interrupt is detected, for instance. + */ +class PreIteration { +public: + /** + * @brief Constructs a PreIteration object that handles locking and + * resizing of nodes. + * + * @param[in] locks A list of nodes (by index) and positions at which + * they should be locked. + * @param[in] resizes A list of nodes (by index) and required dimensions + * for their bounding rects to be resized to. + */ + PreIteration( + Locks& locks=__locksNotUsed, + Resizes& resizes=__resizesNotUsed) + : locks(locks) + , resizes(resizes) + , changed(true) {} + PreIteration(Resizes& resizes) + : locks(__locksNotUsed) + , resizes(resizes) + , changed(true) {} + +// To prevent C++ objects from being destroyed in garbage collected languages +// when the libraries are called from SWIG, we hide the declarations of the +// destructors and prevent generation of default destructors. +#ifndef SWIG + virtual ~PreIteration() {} +#endif + virtual bool operator()() { return true; } + Locks& locks; + Resizes& resizes; + bool changed; +private: + static Locks __locksNotUsed; + static Resizes __resizesNotUsed; +}; + +/** + * @brief A default functor that is called after each iteration of the layout + * algorithm. + * + * You can either instantiate ConstrainedFDLayout with an instance of this + * class setting the tolerance and maxiterations as desired, or create a + * derived class implementing the operator() to do your own convergence test, + * or create your own operator() that calls the TestConvergence::operator() in + * order to do any other post processing you might need, e.g., to animate + * changes. + */ +class TestConvergence { +public: + double old_stress; + TestConvergence(const double tol = 1e-4, const unsigned maxiterations = 100) + : tolerance(tol), + maxiterations(maxiterations) + { reset(); } + virtual ~TestConvergence() {} + +public: + virtual bool operator()(const double new_stress, std::valarray & X, std::valarray & Y) + { + COLA_UNUSED(X); + COLA_UNUSED(Y); + + iterations++; + //std::cout<<"iteration="< const & es, + RootCluster* clusterHierarchy, + const double idealLength, + EdgeLengths eLengths = StandardEdgeLengths, + TestConvergence *doneTest = nullptr, + PreIteration* preIteration=nullptr, + bool useNeighbourStress = false); + /** + * @brief Specify a set of compound constraints to apply to the layout. + * + * @param[in] ccs The compound constraints. + */ + void setConstraints(cola::CompoundConstraints* ccs) { + constrainedLayout = true; + this->ccs=ccs; + } + + void setConstraintsVector(cola::CompoundConstraints& ccs) { + constrainedLayout = true; + cola::CompoundConstraints *ccsp = new cola::CompoundConstraints; + for (size_t i = 0; i < ccs.size(); ++i) { + ccsp->push_back(ccs.at(i)); + } + this->ccs=ccsp; + } + + /** + * @brief Register to receive information about unsatisfiable constraints. + * + * In the case of unsatisifiable constraints, the solver will drop + * unsatisfiable constraints of lowest priority. Information about these + * will be written to these lists after each iteration of constrained + * layout. + * + * param[out] unsatisfiableX A pointer to an UnsatisfiableConstraintInfos + * object that will be used to record + * unsatisfiable constraints in the x-dimension. + * param[out] unsatisfiableY A pointer to an UnsatisfiableConstraintInfos + * object that will be used to record + * unsatisfiable constraints in the y-dimension. + */ + void setUnsatisfiableConstraintInfo( + UnsatisfiableConstraintInfos *unsatisfiableX, + UnsatisfiableConstraintInfos *unsatisfiableY) { + this->unsatisfiableX = unsatisfiableX; + this->unsatisfiableY = unsatisfiableY; + } + /** + * Sticky nodes causes nodes to spring back to (startX,startY) when + * unconstrained. + */ + void setStickyNodes(const double stickyWeight, + std::valarray const & startX, + std::valarray const & startY); + + /** + * Scaling speeds up the solver by conditioning the quadratic terms matrix. + */ + void setScaling(bool scaling) { + this->scaling=scaling; + } + /** + * Says that the Mosek optimisation library should be used to solve the + * quadratic programs rather than the libvpsc solver. + */ + void setExternalSolver(bool externalSolver) { + this->externalSolver=externalSolver; + } + /** + * At each iteration of layout, generate constraints to avoid overlaps. + * If bool horizontal is true, all overlaps will be resolved horizontally, + * otherwise some overlaps will be left to be resolved vertically where + * doing so leads to less displacement + */ + void setAvoidOverlaps(bool horizontal = false) { + constrainedLayout = true; + this->avoidOverlaps = horizontal ? Horizontal : Both; + } + /** + * Add constraints to prevent clusters overlapping. + */ + void setNonOverlappingClusters() { + constrainedLayout = true; + nonOverlappingClusters = true; + } + /** + * For the specified edges (with routings), generate dummy vars and + * constraints to try and straighten them. bendWeight controls how hard we + * try to straighten existing bends potBendWeight controls how much we try + * to keep straight edges straight + */ + void setStraightenEdges(std::vector* straightenEdges, + double bendWeight = 0.01, double potBendWeight = 0.1, + bool xSkipping = true) { + for(std::vector::const_iterator e=straightenEdges->begin(); + e!=straightenEdges->end();e++) { + (*e)->rerouteAround(boundingBoxes); + } + constrainedLayout = true; + this->xSkipping = xSkipping; + this->straightenEdges = straightenEdges; + this->bendWeight = bendWeight; + this->potBendWeight = potBendWeight; + } + /** + * Update position of bounding boxes. + */ + void moveBoundingBoxes() { + for(unsigned i=0;imoveCentre(X[i],Y[i]); + } + } + + ~ConstrainedMajorizationLayout() { + if (using_default_done) + { + delete done; + } + + if(constrainedLayout) { + delete gpX; + delete gpY; + } + } + /** + * @brief Implements the main layout loop, taking descent steps until + * stress is no-longer significantly reduced. + * + * @param[in] x If true, layout will be performed in x-dimension + * (default: true). + * @param[in] y If true, layout will be performed in y-dimension + * (default: true). + */ + void run(bool x=true, bool y=true); + /** + * @brief Same as run(), but only applies one iteration. + * + * This may be useful here it's too hard to implement a call-back + * (e.g., in java apps). + * + * @param[in] x If true, layout will be performed in x-dimension + * (default: true). + * @param[in] y If true, layout will be performed in y-dimension + * (default: true). + */ + void runOnce(bool x=true, bool y=true); + void straighten(std::vector&, vpsc::Dim); + void setConstrainedLayout(bool c) { + constrainedLayout=c; + } + double computeStress(); +private: + double euclidean_distance(unsigned i, unsigned j) { + return sqrt( + (X[i] - X[j]) * (X[i] - X[j]) + + (Y[i] - Y[j]) * (Y[i] - Y[j])); + } + double compute_stress(std::valarray const & Dij); + void majorize(std::valarray const & Dij,GradientProjection* gp, std::valarray& coords, std::valarray const & startCoords); + void newton(std::valarray const & Dij,GradientProjection* gp, std::valarray& coords, std::valarray const & startCoords); + unsigned n; //< number of nodes + //std::valarray degrees; + std::valarray lap2; //< graph laplacian + std::valarray Q; //< quadratic terms matrix used in computations + std::valarray Dij; //< all pairs shortest path distances + double tol; //< convergence tolerance + TestConvergence *done; //< functor used to determine if layout is finished + bool using_default_done; // Whether we allocated a default TestConvergence object. + PreIteration* preIteration; //< client can use this to create locks on nodes + vpsc::Rectangles boundingBoxes; //< node bounding boxes + /* + * stickyNodes controls whether nodes are attracted to their starting + * positions (at time of ConstrainedMajorizationLayout instantiation) + * stored in startX, startY + */ + std::valarray X, Y; + bool stickyNodes; + double stickyWeight; + std::valarray startX; + std::valarray startY; + double edge_length; + bool constrainedLayout; + bool nonOverlappingClusters; + /* + * A cluster is a set of nodes that are somehow semantically grouped + * and should therefore be kept together a bit more tightly than, and + * preferably without overlapping, the rest of the graph. + * + * We achieve this by augmenting the L matrix with stronger attractive + * forces between all members of a cluster (other than the root) + * and by maintaining a (preferably convex) hull around those + * constituents which, using constraints and dummy variables, is + * prevented from overlapping other parts of the graph. + * + * Clusters are defined over the graph in a hierarchy starting with + * a single root cluster. + * + * Need to: + * - augment Lap matrix with intra cluster forces + * - compute convex hull of each cluster + * - from convex hull generate "StraightenEdges" + */ + RootCluster *clusterHierarchy; + GradientProjection *gpX, *gpY; + cola::CompoundConstraints *ccs; + UnsatisfiableConstraintInfos *unsatisfiableX, *unsatisfiableY; + NonOverlapConstraintsMode avoidOverlaps; + std::vector* straightenEdges; + + double bendWeight, potBendWeight; + /* + * determines whether we should leave some overlaps to be resolved + * vertically when generating straightening constraints in the x-dim + */ + bool xSkipping; + /* + * when using the gradient projection optimisation method, the following + * controls whether the problem should be preconditioned by affine scaling + */ + bool scaling; + /* + * if the Mosek quadratic programming environment is available it may be + * used to solve each iteration of stress majorization... slow but useful + * for testing + */ + bool externalSolver; + bool majorization; +}; + +vpsc::Rectangle bounds(vpsc::Rectangles& rs); + +class ConstrainedFDLayout; + +/** + * @brief Interface for writing COLA addons to handle topology preserving + * layout. + */ +class TopologyAddonInterface +{ + public: + TopologyAddonInterface() + { + } + + virtual ~TopologyAddonInterface() + { + } + + virtual TopologyAddonInterface *clone(void) const + { + return new TopologyAddonInterface(*this); + } + + virtual void freeAssociatedObjects(void) + { + } + + virtual void handleResizes(const Resizes& resizeList, unsigned n, + std::valarray& X, std::valarray& Y, + cola::CompoundConstraints& ccs, + vpsc::Rectangles& boundingBoxes, + cola::RootCluster* clusterHierarchy) + { + COLA_UNUSED(resizeList); + COLA_UNUSED(n); + COLA_UNUSED(X); + COLA_UNUSED(Y); + COLA_UNUSED(ccs); + COLA_UNUSED(boundingBoxes); + COLA_UNUSED(clusterHierarchy); + } + virtual void computePathLengths(unsigned short** G) + { + COLA_UNUSED(G); + } + virtual double computeStress(void) const + { + return 0; + } + virtual bool useTopologySolver(void) const + { + return false; + } + virtual void makeFeasible(bool generateNonOverlapConstraints, + vpsc::Rectangles& boundingBoxes, + cola::RootCluster* clusterHierarchy) + { + COLA_UNUSED(generateNonOverlapConstraints); + COLA_UNUSED(boundingBoxes); + COLA_UNUSED(clusterHierarchy); + } + virtual void moveTo(const vpsc::Dim dim, vpsc::Variables& vs, + vpsc::Constraints& cs, std::valarray &coords, + cola::RootCluster* clusterHierarchy) + { + COLA_UNUSED(dim); + COLA_UNUSED(vs); + COLA_UNUSED(cs); + COLA_UNUSED(coords); + COLA_UNUSED(clusterHierarchy); + } + virtual double applyForcesAndConstraints(ConstrainedFDLayout *layout, + const vpsc::Dim dim, std::valarray& g, + vpsc::Variables& vs, vpsc::Constraints& cs, + std::valarray &coords, + DesiredPositionsInDim& des, double oldStress) + { + COLA_UNUSED(layout); + COLA_UNUSED(dim); + COLA_UNUSED(g); + COLA_UNUSED(vs); + COLA_UNUSED(cs); + COLA_UNUSED(coords); + COLA_UNUSED(des); + COLA_UNUSED(oldStress); + return 0; + } +}; + + +/** + * @brief Implements a constrained force-directed layout algorithm. + * + * This method is based on a non-linear gradient projection technique. + * Conceptually it's similar to a force directed method like + * Fruchterman-Reingold---but using a more principled goal function and + * optimisation techniques. + */ +class ConstrainedFDLayout { +public: + /** + * @brief Constructs a constrained force-directed layout instance. + * + * If an empty edges (es) vector is passed to the constructor, then + * constraint satisfaction will be performed, but no force-directed + * layout. In this case, idealLength and eLengths have no effect. + * + * Conversely, if no constraints or clusters are specified and no overlap + * prevention is enabled, but edge info is given, then pure force-directed + * layout will be performed. + * + * @param[in] rs Bounding boxes of nodes at their initial positions. + * @param[in] es Simple pair edges, giving indices of the start and end + * nodes in rs. + * @param[in] idealLength A scalar modifier of ideal edge lengths in + * eLengths or of 1 if no ideal lengths are + * specified. + * @param[in] eLengths Individual ideal lengths for edges. + * The actual ideal length used for the ith edge is + * idealLength*eLengths[i], or if eLengths is nullptr a + * then just idealLength is used (i.e., eLengths[i] + * is assumed to be 1). + * @param[in] done A test of convergence operation called at the end of + * each iteration (optional). If not given, uses a + * default TestConvergence object. + * @param[in] preIteration An operation called before each iteration + * (optional). + */ + ConstrainedFDLayout( + const vpsc::Rectangles& rs, + const std::vector& es, + const double idealLength, + const EdgeLengths& eLengths = StandardEdgeLengths, + TestConvergence* doneTest = nullptr, + PreIteration* preIteration = nullptr); + ~ConstrainedFDLayout(); + + /** + * @brief Implements the main layout loop, taking descent steps until + * stress is no-longer significantly reduced. + * + * @param[in] x If true, layout will be performed in x-dimension + * (default: true). + * @param[in] y If true, layout will be performed in y-dimension + * (default: true). + */ + void run(bool x=true, bool y=true); + + /** + * @brief Same as run(), but only applies one iteration. + * + * This may be useful here it's too hard to implement a call-back + * (e.g., in java apps). + * + * @param[in] x If true, layout will be performed in x-dimension + * (default: true). + * @param[in] y If true, layout will be performed in y-dimension + * (default: true). + */ + void runOnce(bool x=true, bool y=true); + + /** + * @brief Specify a set of compound constraints to apply to the layout. + * + * @param[in] ccs The compound constraints. + */ + void setConstraints(const cola::CompoundConstraints& ccs); + + /** + * @brief Specifies whether non-overlap constraints should be + * automatically generated between all nodes, as well as any + * exemptions to this. + * + * The optional second parameter indicates groups of nodes that should be + * exempt from having non-overlap constraints generated between each other. + * For example, you might want to do this for nodes representing ports, or + * the child nodes in a particular cluster. + * + * @param[in] avoidOverlaps New boolean value for this option. + * @param[in] listOfNodeGroups A list of groups of node indexes which will + * not have non-overlap constraints generated + * between each other. + */ + void setAvoidNodeOverlaps(bool avoidOverlaps, + ListOfNodeIndexes listOfNodeGroups = + ListOfNodeIndexes()); + + /** + * @brief Set an addon for doing topology preserving layout. + * + * It is expected that you would use the topology::ColaTopologyAddon() + * from libtopology rather than write your own. This is done so that + * libcola does not have to depend on libtopology. + * + * @param[in] topology Instance of a class implementing the + * TopologyAddonInterface. + * @sa topology::ColaTopologyAddon + */ + void setTopology(TopologyAddonInterface *topology); + TopologyAddonInterface *getTopology(void); + + void setDesiredPositions(DesiredPositions *desiredPositions); + + /** + * @brief Specifies an optional hierarchy for clustering nodes. + * + * @param[in] hierarchy A pointer to a RootCluster object defining the + * the cluster hierarchy (optional). + */ + void setClusterHierarchy(RootCluster *hierarchy) + { + clusterHierarchy = hierarchy; + } + /** + * @brief Register to receive information about unsatisfiable constraints. + * + * In the case of unsatisifiable constraints, the solver will drop + * unsatisfiable constraints of lowest priority. Information about these + * will be written to these lists after each iteration of constrained + * layout. + * + * param[out] unsatisfiableX A pointer to a UnsatisfiableConstraintInfos + * object that will be used to record + * unsatisfiable constraints in the x-dimension. + * param[out] unsatisfiableY A pointer to a UnsatisfiableConstraintInfos + * object that will be used to record + * unsatisfiable constraints in the y-dimension. + */ + void setUnsatisfiableConstraintInfo( + UnsatisfiableConstraintInfos *unsatisfiableX, + UnsatisfiableConstraintInfos *unsatisfiableY) + { + unsatisfiable.resize(2); + unsatisfiable[0]=unsatisfiableX; + unsatisfiable[1]=unsatisfiableY; + } + /** + * @brief Finds a feasible starting position for nodes that satisfies the + * given constraints. + * + * Starts with an initial position (x, y) for the nodes. This position + * is then iteratively updated with a greedy heuristic that tries adding + * additional constraints based on compound constraint priority to the + * satisfiable set, so as to satisfy as many of the placement constraints + * as possible. This includes automatically generated constraints for + * non-overlap and cluster containment. + * + * @param[in] xBorder Optional border width to add to left and right + * sides of rectangles. Defaults to 1. + * @param[in] yBorder Optional border width to add to top and bottom + * sides of rectangles. Defaults to 1. + * + * @note This method doesn't do force-directed layout. All forces are + * ignored and it merely satisfies the constraints with minimal + * movement to nodes. + */ + void makeFeasible(double xBorder=1, double yBorder=1); + + /** + * @brief A convenience method that can be called from Java to free + * the memory of nodes (Rectangles), CompoundConstraints, etc. + * + * This assumes that the ConstrainedFDLayout instance takes ownership + * of all the objects passed to it. + * + * This is useful because in SWIG we have problems with Java wrapper + * classes going out of scope and causing objects like Rectanges to + * sometimes be freed when the layout instance still needs them. For + * this reason we prevent the Java wrappers from deleting the internal + * C++ instances, and let them be cleaned up later via this method. + */ + void freeAssociatedObjects(void); + + //! @brief Generates an SVG file containing debug output and code that + //! can be used to regenerate the instance. + //! + //! This method can be called before or after run() or makeFeasible() + //! have been called. + //! + //! @param[in] filename A string indicating the filename (without + //! extension) for the output file. Defaults to + //! "libcola-debug.svg" if no filename is given. + //! + void outputInstanceToSVG(std::string filename = std::string()); + + /** + * @brief Specifies whether neighbour stress should be used. + * + * Under neighbour stress, only the terms representing neighbouring + * nodes contribute to the stress function. This can help to distribute + * nodes more evenly, eliminating long-range forces. + * + * Default value is false. + * + * @param[in] useNeighbourStress New boolean value for this option. + */ + void setUseNeighbourStress(bool useNeighbourStress); + + /** + * @brief Retrieve a copy of the "D matrix" computed by the computePathLengths + * method, linearised as a vector. + * + * This is especially useful for projects in SWIG target languages that want to + * do their own computations with stress. + * + * D is the required euclidean distances between pairs of nodes + * based on the shortest paths between them (using + * m_idealEdgeLength*eLengths[edge] as the edge length, if eLengths array + * is provided otherwise just m_idealEdgeLength). + * + * @return vector representing the D matrix. + */ + std::vector readLinearD(void); + + /** + * @brief Retrieve a copy of the "G matrix" computed by the computePathLengths + * method, linearised as a vector. + * + * * This is especially useful for projects in SWIG target languages that want to + * do their own computations with stress. + * + * G is a matrix of unsigned ints such that G[u][v]= + * 0 if there are no forces required between u and v + * (for example, if u and v are in unconnected components) + * 1 if attractive forces are required between u and v + * (i.e. if u and v are immediately connected by an edge and there is + * no topology route between u and v (for which an attractive force + * is computed elsewhere)) + * 2 if no attractive force is required between u and v but there is + * a connected path between them. + * + * @return vector representing the G matrix. + */ + std::vector readLinearG(void); + + double computeStress() const; + +private: + unsigned n; // number of nodes + std::valarray X, Y; + vpsc::Rectangles boundingBoxes; + double applyForcesAndConstraints(const vpsc::Dim dim,const double oldStress); + double computeStepSize(const SparseMatrix& H, const std::valarray& g, + const std::valarray& d) const; + void computeDescentVectorOnBothAxes(const bool xaxis, const bool yaxis, + double stress, std::valarray& x0, std::valarray& x1); + void moveTo(const vpsc::Dim dim, std::valarray& target); + double applyDescentVector( + const std::valarray& d, + const std::valarray& oldCoords, + std::valarray &coords, + const double oldStress, + double stepsize + /*,topology::TopologyConstraints *s=nullptr*/); + void computePathLengths( + const std::vector& es, std::valarray eLengths); + void generateNonOverlapAndClusterCompoundConstraints( + vpsc::Variables (&vs)[2]); + void handleResizes(const Resizes&); + void setPosition(std::valarray& pos); + void moveBoundingBoxes(); + bool noForces(double, double, unsigned) const; + void computeForces(const vpsc::Dim dim, SparseMap &H, + std::valarray &g); + void recGenerateClusterVariablesAndConstraints( + vpsc::Variables (&vars)[2], unsigned int& priority, + cola::NonOverlapConstraints *noc, Cluster *cluster, + cola::CompoundConstraints& idleConstraints); + std::vector offsetDir(double minD); + + void computeNeighbours(std::vector es); + std::vector > neighbours; + std::vector > neighbourLengths; + TestConvergence *done; + bool using_default_done; // Whether we allocated a default TestConvergence object. + PreIteration* preIteration; + cola::CompoundConstraints ccs; + double** D; + unsigned short** G; + double minD; + PseudoRandom random; + + TopologyAddonInterface *topologyAddon; + std::vector unsatisfiable; + bool rungekutta; + DesiredPositions *desiredPositions; + cola::CompoundConstraints extraConstraints; + + RootCluster *clusterHierarchy; + double rectClusterBuffer; + double m_idealEdgeLength; + bool m_generateNonOverlapConstraints; + bool m_useNeighbourStress; + const std::valarray m_edge_lengths; + + NonOverlapConstraintExemptions *m_nonoverlap_exemptions; + + friend class topology::ColaTopologyAddon; + friend class dialect::Graph; +}; + +struct ProjectionResult { + int errorLevel; + std::string unsatinfo; +}; + +/** + * @brief Attempt to do a projection onto a vector of cola CompoundConstraints. + * @param dim the dimension in which to perform the projection + * @param rs the rectangles representing the nodes + * @param ccs the constraints + * @param preventOverlaps boolean saying whether you want overlap prevention + * constraints to be automatically generated + * @param accept an integer indicating which types of infeasibilities you will accept. + * The default value of 0 means you accept no infeasibility. + * For other values, see the description of the "errorLevel" in the + * doctext for the solve function below. + * @param debugLevel see solve function below + * @note Rectangle positions are updated if and only if the error level is less + * than or equal to the accept level. + * @return a ProjectionResult indicating whether the projection was feasible or not. + * @sa solve + */ +ProjectionResult projectOntoCCs(vpsc::Dim dim, vpsc::Rectangles &rs, cola::CompoundConstraints ccs, + bool preventOverlaps, int accept=0, unsigned debugLevel=0); + +/** + * @brief Constructs a solver and attempts to solve the passed constraints on the passed vars. + * @param debugLevel: controls how much information comes back when the projection fails. See below. + * @return a ProjectionResult, containing: + * errorLevel: + * 0: all constraints were satisfiable. + * 1: some constraints were unsatisfiable, but they were all nonoverlap constraints. + * 2: some constraints were unsatisfiable which were /not/ nonoverlap constraints. + * unsatinfo: + * The amount of information reported depends on the debugLevel: + * 0: nothing reported (empty string) + * 1: description of the unsatisfied constraints + * 2: the info from level 1, plus a description of all "related" constraints (those sharing a variable). + * This is useful for understanding the conflicts. + */ +ProjectionResult solve(vpsc::Variables &vs, vpsc::Constraints &cs, vpsc::Rectangles &rs, + unsigned debugLevel=0); + + +ConstrainedMajorizationLayout* simpleCMLFactory( + vpsc::Rectangles& rs, + std::vector const & es, + RootCluster* clusterHierarchy, + const double idealLength, + bool useNeighbourStress = false + ); + +/* + * find shortest path lengths from node s to all other nodes. + * @param s starting node + * @param n total number of nodes + * @param d n vector of path lengths + * @param es edge pairs + * @param eLengths edge weights + */ +void dijkstra(const unsigned s, const unsigned n, double* d, + const std::vector& es, + const std::valarray & eLengths); + +#if 0 +void removeClusterOverlapFast(RootCluster& clusterHierarchy, vpsc::Rectangles& rs, Locks& locks); +#endif + +void setupVarsAndConstraints(unsigned n, const CompoundConstraints& ccs, + const vpsc::Dim dim, vpsc::Rectangles& boundingBoxes, + RootCluster *clusterHierarchy, + vpsc::Variables& vs, vpsc::Constraints& cs, + std::valarray &coords); +void setVariableDesiredPositions(vpsc::Variables& vs, vpsc::Constraints& cs, + const DesiredPositionsInDim& des, std::valarray& coords); + +} +#endif // COLA_H -- cgit v1.2.3