Commit 4dbdc276 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

draw_hierarchy(): Implement "hshortcuts" and "hide" parameters

parent acedc700
...@@ -2150,7 +2150,7 @@ struct enum_from_int ...@@ -2150,7 +2150,7 @@ struct enum_from_int
}; };
void get_cts(GraphInterface& gi, GraphInterface& tgi, boost::any otpos, void get_cts(GraphInterface& gi, GraphInterface& tgi, boost::any otpos,
boost::any obeta, boost::any octs); boost::any obeta, boost::any octs, bool);
BOOST_PYTHON_MODULE(libgraph_tool_draw) BOOST_PYTHON_MODULE(libgraph_tool_draw)
{ {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "graph_properties.hh" #include "graph_properties.hh"
#include <boost/mpl/quote.hpp> #include <boost/mpl/quote.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <cmath> #include <cmath>
...@@ -159,6 +160,32 @@ void tree_path(Graph& g, size_t s, size_t t, vector<size_t>& path) ...@@ -159,6 +160,32 @@ void tree_path(Graph& g, size_t s, size_t t, vector<size_t>& path)
} }
template <class Graph>
void graph_path(Graph& g, size_t s, size_t t, vector<size_t>& path)
{
typename property_map_type::apply<size_t,
typename property_map<Graph, vertex_index_t>::type>::type
cpred;
auto pred = cpred.get_unchecked(num_vertices(g));
UndirectedAdaptor<Graph> ug(g);
boost::breadth_first_search(ug, s,
boost::visitor(
boost::make_bfs_visitor(
boost::record_predecessors(
pred,
boost::on_tree_edge()))));
size_t pos = t;
path.push_back(pos);
while (pos != s)
{
pos = pred[pos];
path.push_back(pos);
}
std::reverse(path.begin(), path.end());
}
template<class T> template<class T>
void pack(vector<point_t>& cp, vector<T>& ncp) void pack(vector<point_t>& cp, vector<T>& ncp)
{ {
...@@ -173,7 +200,7 @@ void pack(vector<point_t>& cp, vector<T>& ncp) ...@@ -173,7 +200,7 @@ void pack(vector<point_t>& cp, vector<T>& ncp)
struct do_get_cts struct do_get_cts
{ {
template <class Graph, class Tree, class PosProp, class BProp, class CMap> template <class Graph, class Tree, class PosProp, class BProp, class CMap>
void operator()(Graph& g, Tree* t, PosProp tpos, BProp beta, CMap cts) const void operator()(Graph& g, Tree* t, PosProp tpos, BProp beta, CMap cts, bool is_tree) const
{ {
vector<size_t> path; vector<size_t> path;
vector<point_t> cp; vector<point_t> cp;
...@@ -187,7 +214,10 @@ struct do_get_cts ...@@ -187,7 +214,10 @@ struct do_get_cts
continue; continue;
path.clear(); path.clear();
tree_path(*t, u, v, path); if (is_tree)
tree_path(*t, u, v, path);
else
graph_path(*t, u, v, path);
cp.clear(); cp.clear();
get_control_points(path, tpos, beta[e], cp); get_control_points(path, tpos, beta[e], cp);
ncp.clear(); ncp.clear();
...@@ -208,8 +238,8 @@ struct get_pointers ...@@ -208,8 +238,8 @@ struct get_pointers
}; };
}; };
void get_cts(GraphInterface& gi, GraphInterface& tgi, void get_cts(GraphInterface& gi, GraphInterface& tgi, boost::any otpos,
boost::any otpos, boost::any obeta, boost::any octs) boost::any obeta, boost::any octs, bool is_tree)
{ {
typedef property_map_type::apply<vector<double>, typedef property_map_type::apply<vector<double>,
GraphInterface::edge_index_map_t>::type GraphInterface::edge_index_map_t>::type
...@@ -221,9 +251,9 @@ void get_cts(GraphInterface& gi, GraphInterface& tgi, ...@@ -221,9 +251,9 @@ void get_cts(GraphInterface& gi, GraphInterface& tgi,
eprop_t cts = boost::any_cast<eprop_t>(octs); eprop_t cts = boost::any_cast<eprop_t>(octs);
beprop_t beta = boost::any_cast<beprop_t>(obeta); beprop_t beta = boost::any_cast<beprop_t>(obeta);
run_action<graph_tool::detail::always_directed, boost::mpl::true_>() run_action<>()
(gi, std::bind(do_get_cts(), placeholders::_1, placeholders::_2, (gi, std::bind(do_get_cts(), placeholders::_1, placeholders::_2,
placeholders::_3, beta, cts), placeholders::_3, beta, cts, is_tree),
get_pointers::apply<graph_tool::detail::always_directed>::type(), get_pointers::apply<graph_tool::detail::always_directed>::type(),
vertex_scalar_vector_properties()) vertex_scalar_vector_properties())
(tgi.GetGraphView(), otpos); (tgi.GetGraphView(), otpos);
......
...@@ -1727,6 +1727,14 @@ def get_hierarchy_tree(state, empty_branches=True): ...@@ -1727,6 +1727,14 @@ def get_hierarchy_tree(state, empty_branches=True):
This transforms a :class:`~graph_tool.NestedBlockState` instance into a This transforms a :class:`~graph_tool.NestedBlockState` instance into a
single :class:`~graph_tool.Graph` instance containing the hierarchy tree. single :class:`~graph_tool.Graph` instance containing the hierarchy tree.
Parameters
----------
state : :class:`~graph_tool.community.NestedBlockState`
Nested block model state.
empty_branches : ``bool`` (optional, default: ``True``)
If ``empty_branches == False``, dangling branches at the upper layers
will be pruned.
Returns Returns
------- -------
...@@ -1738,9 +1746,6 @@ def get_hierarchy_tree(state, empty_branches=True): ...@@ -1738,9 +1746,6 @@ def get_hierarchy_tree(state, empty_branches=True):
order : :class:`~graph_tool.PropertyMap` order : :class:`~graph_tool.PropertyMap`
A vertex property map containing the relative ordering of each layer A vertex property map containing the relative ordering of each layer
according to the total degree of the groups at the specific levels. according to the total degree of the groups at the specific levels.
empty_branches : ``bool`` (optional, default: ``True``)
If ``empty_branches == False``, dangling branches at the upper layers
will be pruned.
""" """
bstack = state.get_bstack() bstack = state.get_bstack()
......
...@@ -27,6 +27,8 @@ import os ...@@ -27,6 +27,8 @@ import os
import warnings import warnings
import numpy import numpy
from .. topology import shortest_distance
try: try:
import cairo import cairo
except ImportError: except ImportError:
...@@ -1224,7 +1226,7 @@ def transform_scale(M, scale): ...@@ -1224,7 +1226,7 @@ def transform_scale(M, scale):
scale / np.sqrt(2)) scale / np.sqrt(2))
return np.sqrt(p[0] ** 2 + p[1] ** 2) return np.sqrt(p[0] ** 2 + p[1] ** 2)
def get_hierarchy_control_points(g, t, tpos, beta=0.8, cts=None): def get_hierarchy_control_points(g, t, tpos, beta=0.8, cts=None, is_tree=True):
r"""Return the Bézier spline control points for the edges in ``g``, given the hierarchical structure encoded in graph `t`. r"""Return the Bézier spline control points for the edges in ``g``, given the hierarchical structure encoded in graph `t`.
Parameters Parameters
...@@ -1248,6 +1250,9 @@ def get_hierarchy_control_points(g, t, tpos, beta=0.8, cts=None): ...@@ -1248,6 +1250,9 @@ def get_hierarchy_control_points(g, t, tpos, beta=0.8, cts=None):
cts : :class:`~graph_tool.PropertyMap` (optional, default: ``None``) cts : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
Edge property map of type ``vector<double>`` where the control points Edge property map of type ``vector<double>`` where the control points
will be stored. will be stored.
is_tree : ``bool`` (optional, default: ``True``)
If ``True``, ``t`` must be a directed tree, otherwise it can be any
connected graph.
Returns Returns
...@@ -1326,7 +1331,8 @@ def get_hierarchy_control_points(g, t, tpos, beta=0.8, cts=None): ...@@ -1326,7 +1331,8 @@ def get_hierarchy_control_points(g, t, tpos, beta=0.8, cts=None):
tu._Graph__graph, tu._Graph__graph,
_prop("v", tu, tpos), _prop("v", tu, tpos),
_prop("e", u, beta), _prop("e", u, beta),
_prop("e", u, cts)) _prop("e", u, cts),
is_tree)
return cts return cts
# #
...@@ -1414,8 +1420,8 @@ class GraphArtist(matplotlib.artist.Artist): ...@@ -1414,8 +1420,8 @@ class GraphArtist(matplotlib.artist.Artist):
def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4, def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4,
halpha=0.6, subsample_edges=None, deg_order=True, halpha=0.6, subsample_edges=None, deg_order=True,
deg_size=True, vsize_scale=1, hsize_scale=1, deg_size=True, vsize_scale=1, hsize_scale=1, hshortcuts=0,
empty_branches=True, verbose=False, **kwargs): hide=0, empty_branches=True, verbose=False, **kwargs):
r"""Draw a nested block model state in a circular hierarchy layout with edge r"""Draw a nested block model state in a circular hierarchy layout with edge
bundling. bundling.
...@@ -1448,14 +1454,19 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4, ...@@ -1448,14 +1454,19 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4,
Multiplicative factor of the vertex sizes. Multiplicative factor of the vertex sizes.
hsize_scale : ``float`` (optional, default: ``1.``) hsize_scale : ``float`` (optional, default: ``1.``)
Multiplicative factor of the sizes of the hierarchy nodes. Multiplicative factor of the sizes of the hierarchy nodes.
hshortcuts : ``int`` (optional, default: ``0``)
Include shortcuts to the number of upper layers in the hierarchy
determined by this parameter.
hide : ``int`` (optional, default: ``0``)
Hide upper levels of the hierarchy.
empty_branches : ``bool`` (optional, default: ``False``) empty_branches : ``bool`` (optional, default: ``False``)
If ``empty_branches == False``, dangling branches at the upper layers If ``empty_branches == False``, dangling branches at the upper layers
will be pruned. will be pruned.
verbose : ``bool`` (optional, default: ``False``) verbose : ``bool`` (optional, default: ``False``)
If ``verbose == True``, verbose information will be displayed. If ``verbose == True``, verbose information will be displayed.
**kwargs : **kwargs :
All remaining keyword arguments will be passed to the All remaining keyword arguments will be passed to the
:func:`~graph_tool.draw.graph_draw` function. :func:`~graph_tool.draw.graph_draw` function.
Returns Returns
------- -------
...@@ -1570,12 +1581,30 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4, ...@@ -1570,12 +1581,30 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4,
else: else:
tpos = t.own_property(layout) tpos = t.own_property(layout)
hvvisible = t.new_vertex_property("bool", True)
if hide > 0:
root = t.vertex(t.num_vertices() - 1)
dist = shortest_distance(t, source=root)
hvvisible.fa = dist.fa >= hide
hevisible = t.new_edge_property("bool", True)
if hshortcuts > 0:
root = t.vertex(t.num_vertices() - 1)
dist = shortest_distance(t, source=root)
nodes = [v for v in t.vertices() if dist[v] <= hshortcuts]
for v in nodes:
for u in nodes:
if u == v:
continue
t.add_edge(u, v)
pos = g.own_property(tpos.copy()) pos = g.own_property(tpos.copy())
if verbose: if verbose:
print("getting cts...") print("getting cts...")
cts = get_hierarchy_control_points(g, t, tpos, beta) cts = get_hierarchy_control_points(g, t, tpos, beta,
is_tree=hshortcuts == 0)
if verbose: if verbose:
print("done.") print("done.")
...@@ -1646,7 +1675,9 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4, ...@@ -1646,7 +1675,9 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4,
tlabels = t.new_vertex_property("string") tlabels = t.new_vertex_property("string")
t_orig = t t_orig = t
t = GraphView(t, vfilt=lambda v: int(v) >= g.num_vertices(True)) t = GraphView(t,
vfilt=lambda v: int(v) >= g.num_vertices(True) and hvvisible[v],
efilt=hevisible)
if verbose: if verbose:
print("joining graphs") print("joining graphs")
props = [(pos, tpos), props = [(pos, tpos),
...@@ -1783,7 +1814,8 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4, ...@@ -1783,7 +1814,8 @@ def draw_hierarchy(state, pos=None, layout="radial", beta=0.8, ealpha=0.4,
vmask = gg.vertex_index.copy("int") vmask = gg.vertex_index.copy("int")
u = GraphView(gg, directed=False, vfilt=vmask.fa < g.num_vertices()) u = GraphView(gg, directed=False, vfilt=vmask.fa < g.num_vertices())
cts = eprops["control_points"] cts = eprops["control_points"]
get_hierarchy_control_points(u, t_orig, pos, beta, cts=cts) get_hierarchy_control_points(u, t_orig, pos, beta, cts=cts,
is_tree=hshortcuts == 0)
def draw_branch(widget, gg, key_id, picked, pos, vprops, eprops): def draw_branch(widget, gg, key_id, picked, pos, vprops, eprops):
if key_id == ord('b'): if key_id == ord('b'):
......
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