1 // Copyright 2004 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
13 #include <boost/graph/graph_traits.hpp>
14 #include <boost/graph/named_function_params.hpp>
15 #include <boost/graph/simple_point.hpp>
18 #include <algorithm> // for std::min and std::max
22 struct square_distance_attractive_force {
23 template<typename Graph, typename T>
25 operator()(typename graph_traits<Graph>::edge_descriptor,
34 struct square_distance_repulsive_force {
35 template<typename Graph, typename T>
37 operator()(typename graph_traits<Graph>::vertex_descriptor,
38 typename graph_traits<Graph>::vertex_descriptor,
48 struct linear_cooling {
49 typedef T result_type;
51 linear_cooling(std::size_t iterations)
52 : temp(T(iterations) / T(10)), step(0.1) { }
54 linear_cooling(std::size_t iterations, T temp)
55 : temp(temp), step(temp / T(iterations)) { }
61 if (temp < T(0)) temp = T(0);
70 struct all_force_pairs
72 template<typename Graph, typename ApplyForce >
73 void operator()(const Graph& g, ApplyForce apply_force)
75 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
76 vertex_iterator v, end;
77 for (tie(v, end) = vertices(g); v != end; ++v) {
78 vertex_iterator u = v;
79 for (++u; u != end; ++u) {
87 template<typename Dim, typename PositionMap>
88 struct grid_force_pairs
90 template<typename Graph>
92 grid_force_pairs(Dim width, Dim height, PositionMap position, const Graph& g)
93 : width(width), height(height), position(position)
95 #ifndef BOOST_NO_STDC_NAMESPACE
97 #endif // BOOST_NO_STDC_NAMESPACE
98 two_k = Dim(2) * sqrt(width*height / num_vertices(g));
101 template<typename Graph, typename ApplyForce >
102 void operator()(const Graph& g, ApplyForce apply_force)
104 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
105 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
106 typedef std::list<vertex_descriptor> bucket_t;
107 typedef std::vector<bucket_t> buckets_t;
109 std::size_t columns = std::size_t(width / two_k + Dim(1));
110 std::size_t rows = std::size_t(height / two_k + Dim(1));
111 buckets_t buckets(rows * columns);
112 vertex_iterator v, v_end;
113 for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
114 std::size_t column = std::size_t((position[*v].x + width / 2) / two_k);
115 std::size_t row = std::size_t((position[*v].y + height / 2) / two_k);
117 if (column >= columns) column = columns - 1;
118 if (row >= rows) row = rows - 1;
119 buckets[row * columns + column].push_back(*v);
122 for (std::size_t row = 0; row < rows; ++row)
123 for (std::size_t column = 0; column < columns; ++column) {
124 bucket_t& bucket = buckets[row * columns + column];
125 typedef typename bucket_t::iterator bucket_iterator;
126 for (bucket_iterator u = bucket.begin(); u != bucket.end(); ++u) {
127 // Repulse vertices in this bucket
128 bucket_iterator v = u;
129 for (++v; v != bucket.end(); ++v) {
134 std::size_t adj_start_row = row == 0? 0 : row - 1;
135 std::size_t adj_end_row = row == rows - 1? row : row + 1;
136 std::size_t adj_start_column = column == 0? 0 : column - 1;
137 std::size_t adj_end_column = column == columns - 1? column : column + 1;
138 for (std::size_t other_row = adj_start_row; other_row <= adj_end_row;
140 for (std::size_t other_column = adj_start_column;
141 other_column <= adj_end_column; ++other_column)
142 if (other_row != row || other_column != column) {
143 // Repulse vertices in this bucket
144 bucket_t& other_bucket
145 = buckets[other_row * columns + other_column];
146 for (v = other_bucket.begin(); v != other_bucket.end(); ++v)
156 PositionMap position;
160 template<typename Dim, typename PositionMap, typename Graph>
161 inline grid_force_pairs<Dim, PositionMap>
162 make_grid_force_pairs(Dim width, Dim height, const PositionMap& position,
164 { return grid_force_pairs<Dim, PositionMap>(width, height, position, g); }
166 template<typename Graph, typename PositionMap, typename Dim>
168 scale_graph(const Graph& g, PositionMap position,
169 Dim left, Dim top, Dim right, Dim bottom)
171 if (num_vertices(g) == 0) return;
178 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
180 // Find min/max ranges
181 Dim minX = position[*vertices(g).first].x, maxX = minX;
182 Dim minY = position[*vertices(g).first].y, maxY = minY;
183 vertex_iterator vi, vi_end;
184 for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
185 BOOST_USING_STD_MIN();
186 BOOST_USING_STD_MAX();
187 minX = min BOOST_PREVENT_MACRO_SUBSTITUTION (minX, position[*vi].x);
188 maxX = max BOOST_PREVENT_MACRO_SUBSTITUTION (maxX, position[*vi].x);
189 minY = min BOOST_PREVENT_MACRO_SUBSTITUTION (minY, position[*vi].y);
190 maxY = max BOOST_PREVENT_MACRO_SUBSTITUTION (maxY, position[*vi].y);
193 // Scale to bounding box provided
194 for (tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
195 position[*vi].x = ((position[*vi].x - minX) / (maxX - minX))
196 * (right - left) + left;
197 position[*vi].y = ((position[*vi].y - minY) / (maxY - minY))
198 * (top - bottom) + bottom;
203 template<typename PositionMap, typename DisplacementMap,
204 typename RepulsiveForce, typename Dim, typename Graph>
205 struct fr_apply_force
207 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
209 fr_apply_force(const PositionMap& position,
210 const DisplacementMap& displacement,
211 RepulsiveForce repulsive_force, Dim k, const Graph& g)
212 : position(position), displacement(displacement),
213 repulsive_force(repulsive_force), k(k), g(g)
216 void operator()(vertex_descriptor u, vertex_descriptor v)
218 #ifndef BOOST_NO_STDC_NAMESPACE
220 #endif // BOOST_NO_STDC_NAMESPACE
222 Dim delta_x = position[v].x - position[u].x;
223 Dim delta_y = position[v].y - position[u].y;
224 Dim dist = sqrt(delta_x * delta_x + delta_y * delta_y);
225 Dim fr = repulsive_force(u, v, k, dist, g);
226 displacement[v].x += delta_x / dist * fr;
227 displacement[v].y += delta_y / dist * fr;
232 PositionMap position;
233 DisplacementMap displacement;
234 RepulsiveForce repulsive_force;
239 } // end namespace detail
241 template<typename Graph, typename PositionMap, typename Dim,
242 typename AttractiveForce, typename RepulsiveForce,
243 typename ForcePairs, typename Cooling, typename DisplacementMap>
245 fruchterman_reingold_force_directed_layout
247 PositionMap position,
250 AttractiveForce attractive_force,
251 RepulsiveForce repulsive_force,
252 ForcePairs force_pairs,
254 DisplacementMap displacement)
256 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
257 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
258 typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
260 #ifndef BOOST_NO_STDC_NAMESPACE
262 #endif // BOOST_NO_STDC_NAMESPACE
264 Dim area = width * height;
265 // assume positions are initialized randomly
266 Dim k = sqrt(area / num_vertices(g));
268 detail::fr_apply_force<PositionMap, DisplacementMap,
269 RepulsiveForce, Dim, Graph>
270 apply_force(position, displacement, repulsive_force, k, g);
274 // Calculate repulsive forces
275 vertex_iterator v, v_end;
276 for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
277 displacement[*v].x = 0;
278 displacement[*v].y = 0;
280 force_pairs(g, apply_force);
282 // Calculate attractive forces
283 edge_iterator e, e_end;
284 for (tie(e, e_end) = edges(g); e != e_end; ++e) {
285 vertex_descriptor v = source(*e, g);
286 vertex_descriptor u = target(*e, g);
287 Dim delta_x = position[v].x - position[u].x;
288 Dim delta_y = position[v].y - position[u].y;
289 Dim dist = sqrt(delta_x * delta_x + delta_y * delta_y);
290 Dim fa = attractive_force(*e, k, dist, g);
292 displacement[v].x -= delta_x / dist * fa;
293 displacement[v].y -= delta_y / dist * fa;
294 displacement[u].x += delta_x / dist * fa;
295 displacement[u].y += delta_y / dist * fa;
299 for (tie(v, v_end) = vertices(g); v != v_end; ++v) {
300 BOOST_USING_STD_MIN();
301 BOOST_USING_STD_MAX();
302 Dim disp_size = sqrt(displacement[*v].x * displacement[*v].x
303 + displacement[*v].y * displacement[*v].y);
304 position[*v].x += displacement[*v].x / disp_size
305 * min BOOST_PREVENT_MACRO_SUBSTITUTION (disp_size, temp);
306 position[*v].y += displacement[*v].y / disp_size
307 * min BOOST_PREVENT_MACRO_SUBSTITUTION (disp_size, temp);
308 position[*v].x = min BOOST_PREVENT_MACRO_SUBSTITUTION
310 max BOOST_PREVENT_MACRO_SUBSTITUTION(-width / 2,
312 position[*v].y = min BOOST_PREVENT_MACRO_SUBSTITUTION
314 max BOOST_PREVENT_MACRO_SUBSTITUTION(-height / 2,
317 } while (temp = cool());
321 template<typename DisplacementMap>
322 struct fr_force_directed_layout
324 template<typename Graph, typename PositionMap, typename Dim,
325 typename AttractiveForce, typename RepulsiveForce,
326 typename ForcePairs, typename Cooling,
327 typename Param, typename Tag, typename Rest>
330 PositionMap position,
333 AttractiveForce attractive_force,
334 RepulsiveForce repulsive_force,
335 ForcePairs force_pairs,
337 DisplacementMap displacement,
338 const bgl_named_params<Param, Tag, Rest>&)
340 fruchterman_reingold_force_directed_layout
341 (g, position, width, height, attractive_force, repulsive_force,
342 force_pairs, cool, displacement);
347 struct fr_force_directed_layout<error_property_not_found>
349 template<typename Graph, typename PositionMap, typename Dim,
350 typename AttractiveForce, typename RepulsiveForce,
351 typename ForcePairs, typename Cooling,
352 typename Param, typename Tag, typename Rest>
355 PositionMap position,
358 AttractiveForce attractive_force,
359 RepulsiveForce repulsive_force,
360 ForcePairs force_pairs,
362 error_property_not_found,
363 const bgl_named_params<Param, Tag, Rest>& params)
365 std::vector<simple_point<Dim> > displacements(num_vertices(g));
366 fruchterman_reingold_force_directed_layout
367 (g, position, width, height, attractive_force, repulsive_force,
369 make_iterator_property_map
370 (displacements.begin(),
371 choose_const_pmap(get_param(params, vertex_index), g,
373 simple_point<Dim>()));
377 } // end namespace detail
379 template<typename Graph, typename PositionMap, typename Dim, typename Param,
380 typename Tag, typename Rest>
382 fruchterman_reingold_force_directed_layout
384 PositionMap position,
387 const bgl_named_params<Param, Tag, Rest>& params)
389 typedef typename property_value<bgl_named_params<Param,Tag,Rest>,
390 vertex_displacement_t>::type D;
392 detail::fr_force_directed_layout<D>::run
393 (g, position, width, height,
394 choose_param(get_param(params, attractive_force_t()),
395 square_distance_attractive_force()),
396 choose_param(get_param(params, repulsive_force_t()),
397 square_distance_repulsive_force()),
398 choose_param(get_param(params, force_pairs_t()),
399 make_grid_force_pairs(width, height, position, g)),
400 choose_param(get_param(params, cooling_t()),
401 linear_cooling<Dim>(100)),
402 get_param(params, vertex_displacement_t()),
406 template<typename Graph, typename PositionMap, typename Dim>
408 fruchterman_reingold_force_directed_layout(const Graph& g,
409 PositionMap position,
413 fruchterman_reingold_force_directed_layout
414 (g, position, width, height,
415 attractive_force(square_distance_attractive_force()));
418 } // end namespace boost
420 #endif // BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP