-
Notifications
You must be signed in to change notification settings - Fork 0
Layout Plug‐in
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.
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.
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.
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.
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.
ELVE is developed and maintained by the Processor Architecture Lab, EPFL.
You can contact us at elve@groupes.epfl.ch.