Commit 6cb45f26 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement get_hierarchy_control_points()

parent ed228c88
......@@ -16,6 +16,7 @@
.. autofunction:: graph_draw
.. autofunction:: graphviz_draw
.. autofunction:: prop_to_size
.. autofunction:: get_hierarchy_control_points
Low-level graph drawing
......
......@@ -15,6 +15,7 @@ libgraph_tool_draw_la_LIBADD = $(MOD_LIBADD)
libgraph_tool_draw_la_LDFLAGS = $(MOD_LDFLAGS) $(CAIROMM_LIBS)
libgraph_tool_draw_la_SOURCES = \
graph_cairo_draw.cc
graph_cairo_draw.cc \
graph_tree_cts.cc
libgraph_tool_draw_la_include_HEADERS =
......@@ -1656,6 +1656,8 @@ struct enum_from_int
}
};
void get_cts(GraphInterface& gi, GraphInterface& tgi,
boost::any otpos, double beta, boost::any octs);
BOOST_PYTHON_MODULE(libgraph_tool_draw)
{
......@@ -1738,6 +1740,8 @@ BOOST_PYTHON_MODULE(libgraph_tool_draw)
enum_from_int<edge_attr_t>();
enum_from_int<vertex_shape_t>();
enum_from_int<edge_marker_t>();
def("get_cts", &get_cts);
}
#else
......
// 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;
typedef pair<double, double> point_t;
point_t interpolate(const point_t& p1, const point_t& p2, double r = 0.5)
{
point_t ret;
ret.first = (1 - r) * p1.first + p2.first * r;
ret.second = (1 - r) * p1.second + p2.second * r;
return ret;
}
void to_bezier(const vector<point_t> &x, vector<point_t>& ncp)
{
vector<point_t> cp(x.size() + 6);
for (size_t i = 0; i < 3; ++i)
cp[i] = x[0];
for (size_t i = 0; i < x.size(); ++i)
cp[i + 3] = x[i];
for (size_t i = cp.size() - 3; i < cp.size(); ++i)
cp[i] = x.back();
vector<point_t> one_thirds(cp.size() - 1);
vector<point_t> two_thirds(cp.size() - 1);
for (size_t i = 0; i < cp.size() - 1; ++i)
{
const point_t& p1 = cp[i];
const point_t& p2 = cp[i + 1];
one_thirds[i] = interpolate(p1, p2, 1./3);
two_thirds[i] = interpolate(p2, p1, 1./3);
}
ncp.resize((cp.size() - 3) * 3);
for (size_t i = 0; i < cp.size() - 3; ++i)
{
size_t pos = i * 3;
ncp[pos] = one_thirds[i + 1];
ncp[pos + 1] = two_thirds[i + 1];
ncp[pos + 2] = interpolate(two_thirds[i + 1], one_thirds[i + 2]);
}
}
void transform(vector<point_t>& cp)
{
point_t origin = cp[0];
for (size_t i = 0; i < cp.size(); ++i)
{
cp[i].first -= origin.first;
cp[i].second -= origin.second;
}
double t = atan2(cp.back().second - cp.front().second,
cp.back().first - cp.front().first);
for (size_t i = 0; i < cp.size(); ++i)
{
double x = cp[i].first;
double y = cp[i].second;
cp[i].first = cos(t) * x + sin(t) * y;
cp[i].second = -sin(t) * x + cos(t) * y;
}
point_t d;
d.first = cp.back().first - cp.front().first;
d.second = cp.back().second - cp.front().second;
double r = sqrt(d.first * d.first + d.second * d.second);
for (size_t i = 0; i < cp.size(); ++i)
cp[i].first /= r;
}
template <class PosProp>
void get_control_points(vector<size_t>& path, PosProp pos, double beta,
vector<point_t>& ncp)
{
size_t L = path.size();
vector<point_t> cp(L);
for (size_t i = 0; i < L; ++i)
cp[i] = make_pair(double(pos[path[i]][0]),
double(pos[path[i]][1]));
ncp.resize(L);
for (size_t i = 0; i < L; ++i)
{
ncp[i].first = beta * cp[i].first + (1 - beta) * (cp[0].first + (cp.back().first - cp[0].first) * i / (L - 1.));
ncp[i].second = beta * cp[i].second + (1 - beta) * (cp[0].second + (cp.back().second - cp[0].second) * i / (L - 1.));
}
}
template <class Graph>
void tree_path(Graph& g, size_t s, size_t t, vector<size_t>& path)
{
vector<size_t> s_root;
vector<size_t> t_root;
s_root.push_back(s);
t_root.push_back(t);
size_t v = s;
size_t u = t;
while (v != u)
{
typename graph_traits<Graph>::in_edge_iterator e, e_end;
tie(e, e_end) = in_edges(v, g);
if (e == e_end)
throw GraphException("Invalid hierarchical tree: No path from source to target.");
v = source(*e, g);
s_root.push_back(v);
tie(e, e_end) = in_edges(u, g);
if (e == e_end)
throw GraphException("Invalid hierarchical tree: No path from source to target.");
u = source(*e, g);
if (u != v)
t_root.push_back(u);
}
path = s_root;
for (typeof(t_root.rbegin()) iter = t_root.rbegin();
iter != t_root.rend(); ++iter)
path.push_back(*iter);
}
template<class T>
void pack(vector<point_t>& cp, vector<T>& ncp)
{
ncp.resize(cp.size() * 2);
for (size_t i = 0; i < cp.size(); ++i)
{
ncp[2 * i] = cp[i].first;
ncp[2 * i + 1] = cp[i].second;
}
}
struct do_get_cts
{
template <class Graph, class Tree, class PosProp, class CMap>
void operator()(Graph& g, Tree* t, PosProp tpos, double beta, CMap cts) const
{
vector<size_t> path;
vector<point_t> cp;
vector<point_t> ncp;
typename graph_traits<Graph>::edge_iterator e, e_end;
for(tie(e, e_end) = edges(g); e != e_end; ++e)
{
typename graph_traits<Graph>::vertex_descriptor u, v;
u = source(*e, g);
v = target(*e, g);
if (u == v)
continue;
path.clear();
tree_path(*t, u, v, path);
cp.clear();
get_control_points(path, tpos, beta, cp);
ncp.clear();
to_bezier(cp, ncp);
transform(ncp);
pack(ncp, cts[*e]);
}
}
};
struct get_pointers
{
template <class List>
struct apply
{
typedef typename mpl::transform<List,
mpl::quote1<add_pointer> >::type type;
};
};
void get_cts(GraphInterface& gi, GraphInterface& tgi,
boost::any otpos, double beta, boost::any octs)
{
typedef property_map_type::apply<vector<double>,
GraphInterface::edge_index_map_t>::type
eprop_t;
eprop_t cts = boost::any_cast<eprop_t>(octs);
run_action<graph_tool::detail::always_directed,mpl::true_>()
(gi, bind<void>(do_get_cts(), _1, _2, _3, beta, cts),
get_pointers::apply<graph_tool::detail::always_directed>::type(),
vertex_scalar_vector_properties())
(tgi.GetGraphView(), otpos);
}
......@@ -35,7 +35,7 @@ Layout algorithms
fruchterman_reingold_layout
arf_layout
random_layout
get_hierarchy_control_points
Graph drawing
=============
......@@ -82,7 +82,7 @@ dl_import("from . import libgraph_tool_layout")
__all__ = ["graph_draw", "graphviz_draw",
"fruchterman_reingold_layout",
"arf_layout", "sfdp_layout", "random_layout",
"cairo_draw", "prop_to_size"]
"cairo_draw", "prop_to_size", "get_hierarchy_control_points"]
def random_layout(g, shape=None, pos=None, dim=2):
......@@ -700,7 +700,7 @@ def sfdp_layout(g, vweight=None, eweight=None, pin=None, groups=None, C=0.2,
return pos
try:
from .cairo_draw import graph_draw, cairo_draw
from .cairo_draw import graph_draw, cairo_draw, get_hierarchy_control_points
except ImportError:
pass
......
......@@ -1009,6 +1009,63 @@ def transform_scale(M, scale):
scale / np.sqrt(2))
return np.sqrt(p[0] ** 2 + p[1] ** 2)
def get_hierarchy_control_points(g, t, tpos, beta=0.8):
r"""Return the Bézier spline control points for the edges in ``g``, given
the hierarchical structure encoded in graph `t`.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be drawn.
t : :class:`~graph_tool.Graph`
Directed graph containing the hierarchy of ``g``. It must be a directed
tree with a single root. The direction of the edges point from the root
to the leaves, and the vertices in ``t`` with index in the range
:math:`[0, N-1]`, with `:math:`N` being the number of vertices in ``g``,
must correspond to the respective vertex in ``g``.
tpos : :class:`~graph_tool.PropertyMap`
Vector-valued vertex property map containing the x and y coordinates of
the vertices in graph ``t``.
beta : ``float`` (optional, default: ``0.8``)
Edge bundling strength. For ``beta == 0`` the edges are straight lines,
and for ``beta == 1`` they strictly follow the hierarchy.
Returns
-------
ctp : :class:`~graph_tool.PropertyMap`
Vector-valued edge property map containing the Bézier spline control
points for the edges in ``g``.
Notes
-----
This is an implementation of the edge-bundling algorithm described in
[holten-hierarchical-2006]_.
Examples
--------
TODO
References
----------
.. [holten-hierarchical-2006] Holten, D. "Hierarchical Edge Bundles:
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`
"""
cts = g.new_edge_property("vector<double>")
u = GraphView(g, directed=True)
tu = GraphView(t, directed=True)
libgraph_tool_draw.get_cts(u._Graph__graph, tu._Graph__graph,
_prop("v", tu, tpos), beta,
_prop("e", u, cts))
return cts
#
# The functions and classes below depend on GTK
......
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