Skip to content

Layout Plug‐in

std::gregwar edited this page Jun 30, 2017 · 6 revisions

The Layout interface allows to implement different layout systems that define the position of the nodes and create different visualisations of the graph.

Default layout plug-in are LevelLayout and SimpleLayout.

Development

The only mandatory method that must be overridden to wrote a Layout plug-in is:

void setGraph(Elve::SharedGraph g) override;

It must initialize the layout plug-in with a skeletal representation of the given graph. While it sounds a bit strange it will become clearer with further reading.

Force-Based Layouts

ELVE comes with a small built-in point based physical engine. Which is directly included in the Elve::LayoutPlugin abstract class. This is the easiest way to create force based layouts in Elve.

Sample code (simplified LevelLayout implementation)

   for(const NodesByID::value_type& p : graph->nodes()) {
        //Use starPosition to obtain either random starting pos or previous layout ending position
        QVector2D pos = startPosition(p.first);
        const Elve::Node& n = p.second;

        qreal mass = 1;qreal damp = 1;
        //Add a point for each node in the graph, FULL here means full repulsive force computation
        Point* m = system().addPoint(mass,p.second.id(),pos,damp,FULL);

        //Pin node if the point is either input or output
        if(p.second.isInput()) {
            int index = p.second.IOIndex();
            system().pin(p.first,{index*ioUnit,inputHeight});
        } else if(p.second.isOutput()) {
            system().pin(p.first,{p.second.IOIndex()*ioUnit,outputHeight});
        } else { //Apply horizontal constraint on point at a certain height corresponding to level
            system().addVConstraint(m,-outputHeight-unit*p.second.level());
        }
    }

This only generates all the points and data related constraints. If, as it is often the case, edges are to be taken in account. A second pass is needed to add spring forces:

    for(const auto& p : graph->nodes()) {
        const Node& g =  p.second;
        Point* ms = system().point(g.id()); //Get the previously created point

        qreal sk = 1.f;
        qreal l0 = 0.f; //usually we don't want edges to have minimum lenght

        for(const Node* n : g.children()) {
            Point* me = system().point(n->id()); //Get point at edge end.
            system().addSpring(ms,me,sk,l0);
        }
    }

It is always possible to add new force type by subclassing Elve::Force and using Elve::System::addForce to add the new force to a point.

Non-Force Based Layouts

When writing non-force based layout, it is still necessary to use the provided system and add points. You can then move theses points arbitrarily by overriding the Elve::LayoutPlugin::tick method to prevent system from simulating the default way. Using Elve::Point::setPos and Elve::Point::notify to apply the change to the visual representation of the nodes.

This is to be better documented and tested.

Usage

To use file loader plug-ins their shared libraries (.so,.dll,.dynlib...) must be placed in:

<elve_root>/plugins/layouts

They are then available through the Layout menu and via the CLI by issuing commands of this form:

<layout_cli_name>_layout [options]

Using CLI or GUI is fully equivalent, as the GUI button will construct the commands and issue them. Modulo the fact that GUI still can't provide [options] which restrain the use of layouts from it.

Clone this wiki locally