Commit 3805add7 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement draw.planar_layout()

parent a3eae6a3
......@@ -9,6 +9,7 @@
.. autofunction:: fruchterman_reingold_layout
.. autofunction:: arf_layout
.. autofunction:: radial_tree_layout
.. autofunction:: planar_layout
.. autofunction:: random_layout
......
......@@ -16,6 +16,7 @@ libgraph_tool_layout_la_LDFLAGS = $(MOD_LDFLAGS)
libgraph_tool_layout_la_SOURCES = \
graph_arf.cc \
graph_planar_layout.cc \
graph_fruchterman_reingold.cc \
graph_sfdp.cc \
graph_radial.cc \
......
......@@ -22,6 +22,7 @@ void export_arf();
void export_fruchterman_reingold();
void export_sfdp();
void export_radial();
void export_planar();
BOOST_PYTHON_MODULE(libgraph_tool_layout)
{
......@@ -29,4 +30,5 @@ BOOST_PYTHON_MODULE(libgraph_tool_layout)
export_fruchterman_reingold();
export_sfdp();
export_radial();
export_planar();
}
// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2016 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_filtering.hh"
#include "graph.hh"
#include "graph_properties.hh"
#include <boost/graph/chrobak_payne_drawing.hpp>
#include <boost/graph/planar_canonical_ordering.hpp>
using namespace std;
using namespace boost;
using namespace graph_tool;
struct point_t
{
size_t x, y;
};
void planar_layout(GraphInterface& gi, boost::any aembed_map, boost::any apos)
{
run_action<graph_tool::detail::never_directed>()
(gi,
[&](auto& g, auto& _embed, auto& _pos)
{
typedef typename std::remove_reference<decltype(g)>::type g_t;
typedef typename graph_traits<g_t>::edge_descriptor edge_t;
typedef typename graph_traits<g_t>::vertex_descriptor vertex_t;
auto eindex = get(edge_index, g);
vector<edge_t> edges;
for (auto e : edges_range(g))
{
auto ei = eindex[e];
if (ei >= edges.size())
edges.resize(ei + 1);
edges[ei] = e;
}
typename vprop_map_t<std::vector<edge_t>>::type::unchecked_t
embed(get(vertex_index, g), num_vertices(g));
parallel_vertex_loop
(g,
[&](auto& v)
{
for (auto ei : _embed[v])
embed[v].push_back(edges[ei]);
});
vector<vertex_t> ordering;
planar_canonical_ordering(g, embed, std::back_inserter(ordering));
assert(ordering.size() >= 3);
typename vprop_map_t<point_t>::type::unchecked_t
pos(get(vertex_index_t(), g), num_vertices(g));
chrobak_payne_straight_line_drawing(g, embed, ordering.begin(),
ordering.end(), pos,
get(vertex_index, g));
parallel_vertex_loop
(g,
[&](auto& v)
{
auto& p = pos[v];
typedef typename std::remove_reference<decltype(_pos[v][0])>::type val_t;
_pos[v] = {val_t(p.x), val_t(p.y)};
});
},
vertex_scalar_vector_properties(), vertex_scalar_vector_properties())
(aembed_map, apos);
}
#include <boost/python.hpp>
void export_planar()
{
boost::python::def("planar_layout", &planar_layout);
}
......@@ -63,17 +63,14 @@ struct get_planar_embedding
boyer_myrvold_params::embedding = embedding,
boyer_myrvold_params::kuratowski_subgraph = kur_insert);
int i, N = num_vertices(g);
#pragma omp parallel for default(shared) private(i) schedule(runtime) if (N > 100)
for (i = 0; i < N; ++i)
{
auto v = vertex(i, g);
if (!is_valid_vertex(v, g))
continue;
embed_map[v].resize(embedding[v].size());
for (size_t j = 0; j < embedding[v].size(); ++j)
embed_map[v][j] = edge_index[embedding[v][j]];
}
parallel_vertex_loop
(g,
[&](auto v)
{
embed_map[v].clear();
for (auto& e : embedding[v])
embed_map[v].push_back(edge_index[e]);
});
}
template <class Graph, class VertexIndex, class EdgeIndex, class KurMap>
......@@ -87,7 +84,6 @@ struct get_planar_embedding
boyer_myrvold_params::edge_index_map = edge_index,
boyer_myrvold_params::kuratowski_subgraph = kur_insert);
}
};
bool is_planar(GraphInterface& gi, boost::any embed_map, boost::any kur_map)
......
......@@ -35,6 +35,7 @@ Layout algorithms
fruchterman_reingold_layout
arf_layout
radial_tree_layout
planar_layout
random_layout
get_hierarchy_control_points
......@@ -67,10 +68,11 @@ Contents
from __future__ import division, absolute_import, print_function
from .. import GraphView, _check_prop_vector, group_vector_property, \
from .. import Graph, 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, shortest_distance
label_components, pseudo_diameter, shortest_distance, make_maximal_planar, \
is_planar
from .. stats import label_parallel_edges
from .. generation import predecessor_tree, condensation_graph
import numpy.random
......@@ -82,9 +84,9 @@ 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",
"default_cm"]
"arf_layout", "sfdp_layout", "planar_layout", "random_layout",
"radial_tree_layout", "cairo_draw", "prop_to_size",
"get_hierarchy_control_points", "default_cm"]
def random_layout(g, shape=None, pos=None, dim=2):
......@@ -156,6 +158,68 @@ def random_layout(g, shape=None, pos=None, dim=2):
return pos
def planar_layout(g, pos=None):
r"""Performs a canonical layout of a planar graph.
Parameters
----------
g : :class:`~graph_tool.Graph`
Planar graph to be used.
pos : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
Vector vertex property maps where the coordinates should be stored.
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
--------
>>> g = gt.lattice([10, 10])
>>> pos = gt.planar_layout(g)
>>> gt.graph_draw(g, pos=pos, output="lattice-planar.pdf")
<...>
.. testcode::
:hide:
gt.graph_draw(g, pos=pos, output="lattice-planar.png")
.. figure:: lattice-planar.*
:align: center
Straight-line drawing of planar graph (a 2D square lattice).
References
----------
.. [straight-line-boost] http://www.boost.org/doc/libs/graph/doc/straight_line_drawing.html
.. [chrobak-linear-1995] M. Chrobak, T. Payne, "A Linear-time Algorithm for
Drawing a Planar Graph on the Grid", Information Processing Letters 54:
241-246, (1995), :doi:`10.1016/0020-0190(95)00020-D`
"""
if g.num_vertices() < 3:
raise ValueError("Graph must have at least 3 vertices.")
if not is_planar(g):
raise ValueError("Graph is not planar.")
u = Graph(GraphView(g, directed=False, skip_properties=True))
make_maximal_planar(u)
embed = is_planar(u, embedding=True)[1]
if pos is None:
pos = u.new_vp("vector<double>")
make_maximal_planar(u)
libgraph_tool_layout.planar_layout(u._Graph__graph,
_prop("v", u, embed),
_prop("v", u, pos))
pos = g.own_property(pos)
return pos
def fruchterman_reingold_layout(g, weight=None, a=None, r=1., scale=None,
circular=False, grid=True, t_range=None,
n_iter=100, pos=None):
......
......@@ -1982,7 +1982,7 @@ def is_planar(g, embedding=False, kuratowski=False):
return tuple(ret)
def make_maximal_planar(g, unfilter=False):
def make_maximal_planar(g):
"""
Add edges to the graph to make it maximally planar.
......@@ -2006,19 +2006,20 @@ def make_maximal_planar(g, unfilter=False):
Examples
--------
>>> g = gt.lattice([42, 42])
>>> g = gt.lattice([10, 10])
>>> gt.make_maximal_planar(g)
>>> gt.is_planar(g)
True
>>> print(g.num_vertices(), g.num_edges())
1764 5286
>>> gt.graph_draw(g, output_size=(300, 300), output="maximal_planar.pdf")
100 294
>>> pos = gt.planar_layout(g)
>>> gt.graph_draw(g, pos, output_size=(300, 300), output="maximal_planar.pdf")
<...>
.. testcode::
:hide:
gt.graph_draw(g, output_size=(300, 300), output="maximal_planar.png")
gt.graph_draw(g, pos, output_size=(300, 300), output="maximal_planar.png")
.. figure:: maximal_planar.*
:align: center
......
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