1 // Copyright 2004, 2005 The Trustees of Indiana University.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
7 // Authors: Douglas Gregor
9 #ifndef BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP
10 #define BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP
12 #include <boost/config/no_tr1/cmath.hpp>
13 #include <boost/graph/graph_traits.hpp>
14 #include <boost/graph/named_function_params.hpp>
15 #include <boost/graph/iteration_macros.hpp>
16 #include <boost/graph/topology.hpp> // For topology concepts
17 #include <boost/graph/detail/mpi_include.hpp>
20 #include <algorithm> // for std::min and std::max
21 #include <numeric> // for std::accumulate
22 #include <cmath> // for std::sqrt and std::fabs
27 struct square_distance_attractive_force {
28 template<typename Graph, typename T>
30 operator()(typename graph_traits<Graph>::edge_descriptor,
39 struct square_distance_repulsive_force {
40 template<typename Graph, typename T>
42 operator()(typename graph_traits<Graph>::vertex_descriptor,
43 typename graph_traits<Graph>::vertex_descriptor,
53 struct linear_cooling {
54 typedef T result_type;
56 linear_cooling(std::size_t iterations)
57 : temp(T(iterations) / T(10)), step(0.1) { }
59 linear_cooling(std::size_t iterations, T temp)
60 : temp(temp), step(temp / T(iterations)) { }
66 if (temp < T(0)) temp = T(0);
75 struct all_force_pairs
77 template<typename Graph, typename ApplyForce >
78 void operator()(const Graph& g, ApplyForce apply_force)
80 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
81 vertex_iterator v, end;
82 for (boost::tie(v, end) = vertices(g); v != end; ++v) {
83 vertex_iterator u = v;
84 for (++u; u != end; ++u) {
92 template<typename Topology, typename PositionMap>
93 struct grid_force_pairs
95 typedef typename property_traits<PositionMap>::value_type Point;
96 BOOST_STATIC_ASSERT (Point::dimensions == 2);
97 typedef typename Topology::point_difference_type point_difference_type;
99 template<typename Graph>
101 grid_force_pairs(const Topology& topology,
102 PositionMap position, const Graph& g)
103 : topology(topology), position(position)
105 two_k = 2. * this->topology.volume(this->topology.extent()) / std::sqrt((double)num_vertices(g));
108 template<typename Graph, typename ApplyForce >
109 void operator()(const Graph& g, ApplyForce apply_force)
111 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
112 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
113 typedef std::list<vertex_descriptor> bucket_t;
114 typedef std::vector<bucket_t> buckets_t;
116 std::size_t columns = std::size_t(topology.extent()[0] / two_k + 1.);
117 std::size_t rows = std::size_t(topology.extent()[1] / two_k + 1.);
118 buckets_t buckets(rows * columns);
119 vertex_iterator v, v_end;
120 for (boost::tie(v, v_end) = vertices(g); v != v_end; ++v) {
122 std::size_t((get(position, *v)[0] + topology.extent()[0] / 2) / two_k);
124 std::size_t((get(position, *v)[1] + topology.extent()[1] / 2) / two_k);
126 if (column >= columns) column = columns - 1;
127 if (row >= rows) row = rows - 1;
128 buckets[row * columns + column].push_back(*v);
131 for (std::size_t row = 0; row < rows; ++row)
132 for (std::size_t column = 0; column < columns; ++column) {
133 bucket_t& bucket = buckets[row * columns + column];
134 typedef typename bucket_t::iterator bucket_iterator;
135 for (bucket_iterator u = bucket.begin(); u != bucket.end(); ++u) {
136 // Repulse vertices in this bucket
137 bucket_iterator v = u;
138 for (++v; v != bucket.end(); ++v) {
143 std::size_t adj_start_row = row == 0? 0 : row - 1;
144 std::size_t adj_end_row = row == rows - 1? row : row + 1;
145 std::size_t adj_start_column = column == 0? 0 : column - 1;
146 std::size_t adj_end_column = column == columns - 1? column : column + 1;
147 for (std::size_t other_row = adj_start_row; other_row <= adj_end_row;
149 for (std::size_t other_column = adj_start_column;
150 other_column <= adj_end_column; ++other_column)
151 if (other_row != row || other_column != column) {
152 // Repulse vertices in this bucket
153 bucket_t& other_bucket
154 = buckets[other_row * columns + other_column];
155 for (v = other_bucket.begin(); v != other_bucket.end(); ++v) {
157 topology.distance(get(position, *u), get(position, *v));
158 if (dist < two_k) apply_force(*u, *v);
166 const Topology& topology;
167 PositionMap position;
171 template<typename PositionMap, typename Topology, typename Graph>
172 inline grid_force_pairs<Topology, PositionMap>
173 make_grid_force_pairs
174 (const Topology& topology,
175 const PositionMap& position, const Graph& g)
176 { return grid_force_pairs<Topology, PositionMap>(topology, position, g); }
178 template<typename Graph, typename PositionMap, typename Topology>
180 scale_graph(const Graph& g, PositionMap position, const Topology& topology,
181 typename Topology::point_type upper_left, typename Topology::point_type lower_right)
183 if (num_vertices(g) == 0) return;
185 typedef typename Topology::point_type Point;
186 typedef typename Topology::point_difference_type point_difference_type;
188 // Find min/max ranges
189 Point min_point = get(position, *vertices(g).first), max_point = min_point;
190 BGL_FORALL_VERTICES_T(v, g, Graph) {
191 min_point = topology.pointwise_min(min_point, get(position, v));
192 max_point = topology.pointwise_max(max_point, get(position, v));
195 Point old_origin = topology.move_position_toward(min_point, 0.5, max_point);
196 Point new_origin = topology.move_position_toward(upper_left, 0.5, lower_right);
197 point_difference_type old_size = topology.difference(max_point, min_point);
198 point_difference_type new_size = topology.difference(lower_right, upper_left);
200 // Scale to bounding box provided
201 BGL_FORALL_VERTICES_T(v, g, Graph) {
202 point_difference_type relative_loc = topology.difference(get(position, v), old_origin);
203 relative_loc = (relative_loc / old_size) * new_size;
204 put(position, v, topology.adjust(new_origin, relative_loc));
209 template<typename Topology, typename PropMap, typename Vertex>
211 maybe_jitter_point(const Topology& topology,
212 const PropMap& pm, Vertex v,
213 const typename Topology::point_type& p2)
215 double too_close = topology.norm(topology.extent()) / 10000.;
216 if (topology.distance(get(pm, v), p2) < too_close) {
218 topology.move_position_toward(get(pm, v), 1./200,
219 topology.random_point()));
223 template<typename Topology, typename PositionMap, typename DisplacementMap,
224 typename RepulsiveForce, typename Graph>
225 struct fr_apply_force
227 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
228 typedef typename Topology::point_type Point;
229 typedef typename Topology::point_difference_type PointDiff;
231 fr_apply_force(const Topology& topology,
232 const PositionMap& position,
233 const DisplacementMap& displacement,
234 RepulsiveForce repulsive_force, double k, const Graph& g)
235 : topology(topology), position(position), displacement(displacement),
236 repulsive_force(repulsive_force), k(k), g(g)
239 void operator()(vertex_descriptor u, vertex_descriptor v)
242 // When the vertices land on top of each other, move the
243 // first vertex away from the boundaries.
244 maybe_jitter_point(topology, position, u, get(position, v));
246 double dist = topology.distance(get(position, u), get(position, v));
247 typename Topology::point_difference_type dispv = get(displacement, v);
249 for (std::size_t i = 0; i < Point::dimensions; ++i) {
253 double fr = repulsive_force(u, v, k, dist, g);
254 dispv += (fr / dist) *
255 topology.difference(get(position, v), get(position, u));
257 put(displacement, v, dispv);
262 const Topology& topology;
263 PositionMap position;
264 DisplacementMap displacement;
265 RepulsiveForce repulsive_force;
270 } // end namespace detail
272 template<typename Topology, typename Graph, typename PositionMap,
273 typename AttractiveForce, typename RepulsiveForce,
274 typename ForcePairs, typename Cooling, typename DisplacementMap>
276 fruchterman_reingold_force_directed_layout
278 PositionMap position,
279 const Topology& topology,
280 AttractiveForce attractive_force,
281 RepulsiveForce repulsive_force,
282 ForcePairs force_pairs,
284 DisplacementMap displacement)
286 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
287 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
288 typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
290 double volume = topology.volume(topology.extent());
292 // assume positions are initialized randomly
293 double k = pow(volume / num_vertices(g), 1. / (double)(Topology::point_difference_type::dimensions));
295 detail::fr_apply_force<Topology, PositionMap, DisplacementMap,
296 RepulsiveForce, Graph>
297 apply_force(topology, position, displacement, repulsive_force, k, g);
300 // Calculate repulsive forces
301 vertex_iterator v, v_end;
302 for (boost::tie(v, v_end) = vertices(g); v != v_end; ++v)
303 put(displacement, *v, typename Topology::point_difference_type());
304 force_pairs(g, apply_force);
306 // Calculate attractive forces
307 edge_iterator e, e_end;
308 for (boost::tie(e, e_end) = edges(g); e != e_end; ++e) {
309 vertex_descriptor v = source(*e, g);
310 vertex_descriptor u = target(*e, g);
312 // When the vertices land on top of each other, move the
313 // first vertex away from the boundaries.
314 ::boost::detail::maybe_jitter_point(topology, position, u, get(position, v));
316 typename Topology::point_difference_type delta =
317 topology.difference(get(position, v), get(position, u));
318 double dist = topology.distance(get(position, u), get(position, v));
319 double fa = attractive_force(*e, k, dist, g);
321 put(displacement, v, get(displacement, v) - (fa / dist) * delta);
322 put(displacement, u, get(displacement, u) + (fa / dist) * delta);
325 if (double temp = cool()) {
327 BGL_FORALL_VERTICES_T (v, g, Graph) {
328 BOOST_USING_STD_MIN();
329 BOOST_USING_STD_MAX();
330 double disp_size = topology.norm(get(displacement, v));
331 put(position, v, topology.adjust(get(position, v), get(displacement, v) * (min BOOST_PREVENT_MACRO_SUBSTITUTION (disp_size, temp) / disp_size)));
332 put(position, v, topology.bound(get(position, v)));
341 template<typename DisplacementMap>
342 struct fr_force_directed_layout
344 template<typename Topology, typename Graph, typename PositionMap,
345 typename AttractiveForce, typename RepulsiveForce,
346 typename ForcePairs, typename Cooling,
347 typename Param, typename Tag, typename Rest>
350 PositionMap position,
351 const Topology& topology,
352 AttractiveForce attractive_force,
353 RepulsiveForce repulsive_force,
354 ForcePairs force_pairs,
356 DisplacementMap displacement,
357 const bgl_named_params<Param, Tag, Rest>&)
359 fruchterman_reingold_force_directed_layout
360 (g, position, topology, attractive_force, repulsive_force,
361 force_pairs, cool, displacement);
366 struct fr_force_directed_layout<param_not_found>
368 template<typename Topology, typename Graph, typename PositionMap,
369 typename AttractiveForce, typename RepulsiveForce,
370 typename ForcePairs, typename Cooling,
371 typename Param, typename Tag, typename Rest>
374 PositionMap position,
375 const Topology& topology,
376 AttractiveForce attractive_force,
377 RepulsiveForce repulsive_force,
378 ForcePairs force_pairs,
381 const bgl_named_params<Param, Tag, Rest>& params)
383 typedef typename Topology::point_difference_type PointDiff;
384 std::vector<PointDiff> displacements(num_vertices(g));
385 fruchterman_reingold_force_directed_layout
386 (g, position, topology, attractive_force, repulsive_force,
388 make_iterator_property_map
389 (displacements.begin(),
390 choose_const_pmap(get_param(params, vertex_index), g,
396 } // end namespace detail
398 template<typename Topology, typename Graph, typename PositionMap, typename Param,
399 typename Tag, typename Rest>
401 fruchterman_reingold_force_directed_layout
403 PositionMap position,
404 const Topology& topology,
405 const bgl_named_params<Param, Tag, Rest>& params)
407 typedef typename get_param_type<vertex_displacement_t, bgl_named_params<Param,Tag,Rest> >::type D;
409 detail::fr_force_directed_layout<D>::run
410 (g, position, topology,
411 choose_param(get_param(params, attractive_force_t()),
412 square_distance_attractive_force()),
413 choose_param(get_param(params, repulsive_force_t()),
414 square_distance_repulsive_force()),
415 choose_param(get_param(params, force_pairs_t()),
416 make_grid_force_pairs(topology, position, g)),
417 choose_param(get_param(params, cooling_t()),
418 linear_cooling<double>(100)),
419 get_param(params, vertex_displacement_t()),
423 template<typename Topology, typename Graph, typename PositionMap>
425 fruchterman_reingold_force_directed_layout
427 PositionMap position,
428 const Topology& topology)
430 fruchterman_reingold_force_directed_layout
431 (g, position, topology,
432 attractive_force(square_distance_attractive_force()));
435 } // end namespace boost
437 #include BOOST_GRAPH_MPI_INCLUDE(<boost/graph/distributed/fruchterman_reingold.hpp>)
439 #endif // BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP