Commit ae9857c0 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement radial_tree_layout()

parent 30dee452
......@@ -6,6 +6,7 @@
.. autofunction:: sfdp_layout
.. autofunction:: fruchterman_reingold_layout
.. autofunction:: arf_layout
.. autofunction:: radial_tree_layout
.. autofunction:: random_layout
......
......@@ -18,6 +18,7 @@ libgraph_tool_layout_la_SOURCES = \
graph_arf.cc \
graph_fruchterman_reingold.cc \
graph_sfdp.cc \
graph_radial.cc \
graph_bind_layout.cc
libgraph_tool_layout_la_include_HEADERS = \
......
......@@ -21,10 +21,12 @@ using namespace boost::python;
void export_arf();
void export_fruchterman_reingold();
void export_sfdp();
void export_radial();
BOOST_PYTHON_MODULE(libgraph_tool_layout)
{
export_arf();
export_fruchterman_reingold();
export_sfdp();
export_radial();
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2013 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program 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. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graph.hh"
#include "graph_filtering.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
#include <cmath>
using namespace std;
using namespace boost;
using namespace graph_tool;
struct do_get_radial
{
template <class Graph, class PosProp, class LevelMap>
void operator()(Graph& g, PosProp tpos, LevelMap level, size_t root,
bool weighted, double r) const
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
typedef property_map_type::apply<int, GraphInterface::vertex_index_map_t>::type
vcount_t;
vcount_t::unchecked_t count(get(vertex_index, g), num_vertices(g));
if (!weighted)
{
typename graph_traits<Graph>::vertex_iterator v, v_end;
for(tie(v, v_end) = vertices(g); v != v_end; ++v)
count[*v] = 1;
}
else
{
deque<vertex_t> q;
typename graph_traits<Graph>::vertex_iterator v, v_end;
for(tie(v, v_end) = vertices(g); v != v_end; ++v)
if (out_degree(*v, g) == 0)
{
q.push_back(*v);
count[*v] = 1;
}
typedef property_map_type::apply<uint8_t, GraphInterface::vertex_index_map_t>::type
vmark_t;
vmark_t::unchecked_t mark(get(vertex_index, g), num_vertices(g));
while (!q.empty())
{
vertex_t v = q.front();
q.pop_front();
typename graph_traits<Graph>::in_edge_iterator e, e_end;
for(tie(e, e_end) = in_edges(v, g); e != e_end; ++e)
{
vertex_t w = source(*e, g);
count[w] += count[v];
if (!mark[w])
{
q.push_back(w);
mark[w] = true;
}
}
}
}
vector<vector<vertex_t> > layers(1);
layers[0].push_back(root);
bool last = false;
while (!last)
{
layers.resize(layers.size() + 1);
vector<vertex_t>& new_layer = layers[layers.size() - 1];
vector<vertex_t>& last_layer = layers[layers.size() - 2];
last = true;
for (size_t i = 0; i < last_layer.size(); ++i)
{
vertex_t v = last_layer[i];
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for(tie(e, e_end) = out_edges(v, g); e != e_end; ++e)
{
vertex_t w = target(*e, g);
new_layer.push_back(w);
if (layers.size() - 1 == level[w])
last = false;
}
if (out_degree(v, g) == 0)
new_layer.push_back(v);
}
if (last)
layers.pop_back();
}
typedef property_map_type::apply<double, GraphInterface::vertex_index_map_t>::type
vangle_t;
vangle_t::unchecked_t angle(get(vertex_index, g), num_vertices(g));
double d_sum = 0;
vector<vertex_t>& outer_layer = layers.back();
for (size_t i = 0; i < outer_layer.size(); ++i)
d_sum += count[outer_layer[i]];
for (size_t i = 0; i < outer_layer.size(); ++i)
angle[outer_layer[i]] = (i * 2 * M_PI * count[outer_layer[i]]) / d_sum;
for (size_t i = 0; i < layers.size(); ++i)
{
vector<vertex_t>& vs = layers[layers.size() - 1 - i];
for (size_t j = 0; j < vs.size(); ++j)
{
vertex_t v = vs[j];
d_sum = 0;
typename graph_traits<Graph>::out_edge_iterator e, e_end;
for(tie(e, e_end) = out_edges(v, g); e != e_end; ++e)
{
vertex_t w = target(*e, g);
d_sum += count[w];
}
for(tie(e, e_end) = out_edges(v, g); e != e_end; ++e)
{
vertex_t w = target(*e, g);
angle[v] += angle[w] * count[w] / d_sum;
}
double d = level[v] * r;
tpos[v].resize(2);
tpos[v][0] = d * cos(angle[v]);
tpos[v][1] = d * sin(angle[v]);
}
}
}
};
void get_radial(GraphInterface& gi, boost::any otpos, boost::any olevels,
size_t root, bool weighted, double r)
{
run_action<graph_tool::detail::always_directed>()
(gi, bind<void>(do_get_radial(), _1, _2, _3, root, weighted, r),
vertex_scalar_vector_properties(),
vertex_scalar_properties())(otpos, olevels);
}
#include <boost/python.hpp>
void export_radial()
{
python::def("get_radial", &get_radial);
}
......@@ -34,6 +34,7 @@ Layout algorithms
sfdp_layout
fruchterman_reingold_layout
arf_layout
radial_tree_layout
random_layout
get_hierarchy_control_points
......@@ -68,9 +69,10 @@ from __future__ import division, absolute_import, print_function
from .. import GraphView, _check_prop_vector, group_vector_property, \
ungroup_vector_property, infect_vertex_property, _prop, _get_rng
from .. topology import max_cardinality_matching, max_independent_vertex_set, \
label_components, pseudo_diameter
label_components, pseudo_diameter, shortest_distance
from .. community import condensation_graph
from .. stats import label_parallel_edges
from .. generation import predecessor_tree
import numpy.random
from numpy import sqrt
import sys
......@@ -82,6 +84,7 @@ dl_import("from . import libgraph_tool_layout")
__all__ = ["graph_draw", "graphviz_draw",
"fruchterman_reingold_layout",
"arf_layout", "sfdp_layout", "random_layout",
"radial_tree_layout",
"cairo_draw", "prop_to_size", "get_hierarchy_control_points"]
......@@ -699,6 +702,69 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, groups=None, C=0.2,
verbose)
return pos
def radial_tree_layout(g, root, weighted=False, r=1.):
r"""Computes a radial layout of the graph according to the minimum spanning
tree centered at the ``root`` vertex.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
root : :class:`~graph_tool.Vertex` or ``int``
The root of the radial tree.
weighted : ``bool`` (optional, default: ``False``)
If true, the angle between the child branches will be computed according
to weight of the entire sub-branches.
r : ``float`` (optional, default: ``1.``)
Layer spacing.
Returns
-------
pos : :class:`~graph_tool.PropertyMap`
A vector-valued vertex property map with the coordinates of the
vertices.
Notes
-----
This algorithm has complexity :math:`O(V + E)`.
Examples
--------
.. testcode::
:hide:
np.random.seed(42)
gt.seed_rng(42)
>>> g = gt.price_network(1000)
>>> pos = gt.radial_tree_layout(g, g.vertex(0))
>>> gt.graph_draw(g, pos=pos, output="graph-draw-radial.pdf")
<...>
.. testcode::
:hide:
gt.graph_draw(g, pos=pos, output="graph-draw-radial.png")
.. figure:: graph-draw-radial.*
:align: center
Radial tree layout of a Price network.
"""
levels, pred_map = shortest_distance(GraphView(g, directed=False), root,
pred_map=True)
t = predecessor_tree(g, pred_map)
pos = t.new_vertex_property("vector<double>")
levels = t.own_property(levels)
libgraph_tool_layout.get_radial(t._Graph__graph,
_prop("v", g, pos),
_prop("v", g, levels),
int(root), weighted, r)
return g.own_property(pos)
try:
from .cairo_draw import graph_draw, cairo_draw, get_hierarchy_control_points
except ImportError:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment