Commit 9f7ea589 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

draw_hierarchy(): Add support for node relative ordering

parent c419b2cb
......@@ -34,7 +34,8 @@ struct do_get_radial
template <class Graph, class PosProp, class LevelMap, class OrderMap,
class WeightMap>
void operator()(Graph& g, PosProp tpos, LevelMap level, OrderMap order,
WeightMap weight, size_t root, bool weighted, double r) const
WeightMap weight, size_t root, bool weighted, double r,
bool order_propagate) const
{
typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
typedef typename vprop_map_t<typename property_traits<WeightMap>::value_type>::type vcount_t;
......@@ -77,6 +78,32 @@ struct do_get_radial
}
}
vprop_map_t<double>::type::unchecked_t vorder(get(vertex_index, g));
if (order_propagate)
{
vorder.resize(num_vertices(g));
std::vector<size_t> vs(vertices(g).first, vertices(g).second);
std::sort(vs.begin(), vs.end(),
[&] (vertex_t u, vertex_t v) { return order[u] < order[v]; });
for (size_t i = 0; i < vs.size(); ++i)
vorder[vs[i]] = i;
std::sort(vs.begin(), vs.end(),
[&] (vertex_t u, vertex_t v) { return level[u] > level[v]; });
for (auto v : vs)
{
if (out_degree(v,g) == 0)
continue;
vorder[v] = 0;
for (auto e : out_edges_range(v, g))
vorder[v] += vorder[target(e,g)];
vorder[v] /= out_degree(v,g);
}
}
vector<vector<vertex_t>> layers(1);
layers[0].push_back(root);
......@@ -100,8 +127,20 @@ struct do_get_radial
last = false;
}
std::sort(new_layer.end() - out_degree(v, g), new_layer.end(),
[&] (vertex_t u, vertex_t v) -> bool { return order[u] < order[v]; });
if (order_propagate)
{
std::sort(new_layer.end() - out_degree(v, g),
new_layer.end(),
[&] (vertex_t u, vertex_t v)
{ return vorder[u] < vorder[v]; });
}
else
{
std::sort(new_layer.end() - out_degree(v, g),
new_layer.end(),
[&] (vertex_t u, vertex_t v)
{ return order[u] < order[v]; });
}
if (out_degree(v, g) == 0)
new_layer.push_back(v);
......@@ -153,7 +192,7 @@ struct do_get_radial
void get_radial(GraphInterface& gi, boost::any otpos, boost::any olevels,
boost::any oorder, boost::any oweight, size_t root,
bool weighted, double r)
bool weighted, double r, bool order_propagate)
{
typedef vprop_map_t<int32_t>::type vmap_t;
......@@ -165,7 +204,8 @@ void get_radial(GraphInterface& gi, boost::any otpos, boost::any olevels,
run_action<graph_tool::detail::always_directed>()
(gi, std::bind(do_get_radial(), std::placeholders::_1, std::placeholders::_2,
levels, std::placeholders::_3, weight, root, weighted, r),
levels, std::placeholders::_3, weight, root, weighted, r,
order_propagate),
vertex_scalar_vector_properties(),
vertex_properties())(otpos, oorder);
}
......
......@@ -767,8 +767,8 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, groups=None, C=0.2,
pos = g_.own_property(pos)
return pos
def radial_tree_layout(g, root, rel_order=None, weighted=False,
node_weight=None, r=1.):
def radial_tree_layout(g, root, rel_order=None, rel_order_leaf=False,
weighted=False, node_weight=None, r=1.):
r"""Computes a radial layout of the graph according to the minimum spanning
tree centered at the ``root`` vertex.
......@@ -780,6 +780,9 @@ def radial_tree_layout(g, root, rel_order=None, weighted=False,
The root of the radial tree.
rel_order : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
Relative order of the nodes at each respective branch.
rel_order_leaf : ``bool`` (optional, default: ``False``)
If ``True``, the relative order of the leafs will propagate to the
root. Otherwise they will propagate in the opposite direction.
weighted : ``bool`` (optional, default: ``False``)
If true, the angle between the child branches will be computed according
to weight of the entire sub-branches.
......@@ -797,7 +800,8 @@ def radial_tree_layout(g, root, rel_order=None, weighted=False,
Notes
-----
This algorithm has complexity :math:`O(V + E)`.
This algorithm has complexity :math:`O(V + E)`, or :math:`O(V\log V + E)` if
``rel_order`` is given.
Examples
--------
......@@ -840,7 +844,8 @@ def radial_tree_layout(g, root, rel_order=None, weighted=False,
_prop("v", g, levels),
_prop("v", g, rel_order),
_prop("v", g, node_weight),
int(root), weighted, r)
int(root), weighted, r,
rel_order_leaf)
return g.own_property(pos)
def prop_to_size(prop, mi=0, ma=5, log=False, power=0.5):
......
......@@ -1560,7 +1560,7 @@ class GraphArtist(matplotlib.artist.Artist):
def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, node_weight=None,
vprops=None, eprops=None, hvprops=None, heprops=None,
subsample_edges=None, deg_order=True, deg_size=True,
subsample_edges=None, rel_order="degree", deg_size=True,
vsize_scale=1, hsize_scale=1, hshortcuts=0, hide=0,
bip_aspect=1., empty_branches=False, **kwargs):
r"""Draw a nested block model state in a circular hierarchy layout with edge
......@@ -1605,9 +1605,11 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, node_weight=None,
subsample_edges : ``int`` or list of :class:`~graph_tool.Edge` instances (optional, default: ``None``)
If provided, only this number of random edges will be drawn. If the
value is a list, it should include the edges that are to be drawn.
deg_order : ``bool`` (optional, default: ``True``)
If ``True``, the vertices will be ordered according to degree inside
each group.
rel_order : ``str`` or ``None`` or :class:`~graph_tool.PropertyMap` (optional, default: ``"degree"``)
If ``degree``, the vertices will be ordered according to degree inside
each group, and the relative ordering of the hierarchy branches. If
instead a :class:`~graph_tool.PropertyMap` is provided, its value will
be used for the relative ordering.
deg_size : ``bool`` (optional, default: ``True``)
If ``True``, the (total) node degrees will be used for the default
vertex sizes..
......@@ -1690,6 +1692,7 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, node_weight=None,
Visualization of Adjacency Relations in Hierarchical Data.", IEEE
Transactions on Visualization and Computer Graphics 12, no. 5, 741–748
(2006). :doi:`10.1109/TVCG.2006.147`
"""
g = state.g
......@@ -1718,12 +1721,13 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, node_weight=None,
emask[e] = True
g = GraphView(g, efilt=emask)
t, tb, vorder = get_hierarchy_tree(state,
empty_branches=empty_branches)
t, tb, tvorder = get_hierarchy_tree(state,
empty_branches=empty_branches)
if layout == "radial":
if not deg_order:
vorder = None
if rel_order == "degree":
rel_order = g.degree_property_map("total")
vorder = t.own_property(rel_order.copy())
if pos is not None:
x, y = ungroup_vector_property(pos, [0, 1])
x.fa -= x.fa.mean()
......@@ -1737,7 +1741,8 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, node_weight=None,
tpos = radial_tree_layout(t, root=t.vertex(t.num_vertices() - 1,
use_index=False),
node_weight=node_weight,
rel_order=vorder)
rel_order=vorder,
rel_order_leaf=True)
elif layout == "bipartite":
tpos = get_bip_hierachy_pos(state, aspect=bip_aspect,
node_weight=node_weight)
......@@ -2007,7 +2012,12 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, node_weight=None,
if "eorder" in kwargs:
kwargs["eorder"] = eorder
pos = graph_draw(u, pos, vprops=t_vprops, eprops=t_eprops, vorder=vorder,
vorder = kwargs.pop("vorder", None)
if vorder is None:
vorder = g.degree_property_map("total")
tvorder.fa[:g.num_vertices()] = vorder.fa
pos = graph_draw(u, pos, vprops=t_vprops, eprops=t_eprops, vorder=tvorder,
**kwargs)
if isinstance(pos, PropertyMap):
......
Supports Markdown
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