|
1 // Copyright 2004 The Trustees of Indiana University. |
|
2 |
|
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) |
|
6 |
|
7 // Authors: Douglas Gregor |
|
8 // Andrew Lumsdaine |
|
9 #ifndef BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP |
|
10 #define BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP |
|
11 |
|
12 #include <cmath> |
|
13 #include <boost/graph/graph_traits.hpp> |
|
14 #include <boost/graph/named_function_params.hpp> |
|
15 #include <boost/graph/simple_point.hpp> |
|
16 #include <vector> |
|
17 #include <list> |
|
18 #include <algorithm> // for std::min and std::max |
|
19 |
|
20 namespace boost { |
|
21 |
|
22 struct square_distance_attractive_force { |
|
23 template<typename Graph, typename T> |
|
24 T |
|
25 operator()(typename graph_traits<Graph>::edge_descriptor, |
|
26 T k, |
|
27 T d, |
|
28 const Graph&) const |
|
29 { |
|
30 return d * d / k; |
|
31 } |
|
32 }; |
|
33 |
|
34 struct square_distance_repulsive_force { |
|
35 template<typename Graph, typename T> |
|
36 T |
|
37 operator()(typename graph_traits<Graph>::vertex_descriptor, |
|
38 typename graph_traits<Graph>::vertex_descriptor, |
|
39 T k, |
|
40 T d, |
|
41 const Graph&) const |
|
42 { |
|
43 return k * k / d; |
|
44 } |
|
45 }; |
|
46 |
|
47 template<typename T> |
|
48 struct linear_cooling { |
|
49 typedef T result_type; |
|
50 |
|
51 linear_cooling(std::size_t iterations) |
|
52 : temp(T(iterations) / T(10)), step(0.1) { } |
|
53 |
|
54 linear_cooling(std::size_t iterations, T temp) |
|
55 : temp(temp), step(temp / T(iterations)) { } |
|
56 |
|
57 T operator()() |
|
58 { |
|
59 T old_temp = temp; |
|
60 temp -= step; |
|
61 if (temp < T(0)) temp = T(0); |
|
62 return old_temp; |
|
63 } |
|
64 |
|
65 private: |
|
66 T temp; |
|
67 T step; |
|
68 }; |
|
69 |
|
70 struct all_force_pairs |
|
71 { |
|
72 template<typename Graph, typename ApplyForce > |
|
73 void operator()(const Graph& g, ApplyForce apply_force) |
|
74 { |
|
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) { |
|
80 apply_force(*u, *v); |
|
81 apply_force(*v, *u); |
|
82 } |
|
83 } |
|
84 } |
|
85 }; |
|
86 |
|
87 template<typename Dim, typename PositionMap> |
|
88 struct grid_force_pairs |
|
89 { |
|
90 template<typename Graph> |
|
91 explicit |
|
92 grid_force_pairs(Dim width, Dim height, PositionMap position, const Graph& g) |
|
93 : width(width), height(height), position(position) |
|
94 { |
|
95 #ifndef BOOST_NO_STDC_NAMESPACE |
|
96 using std::sqrt; |
|
97 #endif // BOOST_NO_STDC_NAMESPACE |
|
98 two_k = Dim(2) * sqrt(width*height / num_vertices(g)); |
|
99 } |
|
100 |
|
101 template<typename Graph, typename ApplyForce > |
|
102 void operator()(const Graph& g, ApplyForce apply_force) |
|
103 { |
|
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; |
|
108 |
|
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); |
|
116 |
|
117 if (column >= columns) column = columns - 1; |
|
118 if (row >= rows) row = rows - 1; |
|
119 buckets[row * columns + column].push_back(*v); |
|
120 } |
|
121 |
|
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) { |
|
130 apply_force(*u, *v); |
|
131 apply_force(*v, *u); |
|
132 } |
|
133 |
|
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; |
|
139 ++other_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) |
|
147 apply_force(*u, *v); |
|
148 } |
|
149 } |
|
150 } |
|
151 } |
|
152 |
|
153 private: |
|
154 Dim width; |
|
155 Dim height; |
|
156 PositionMap position; |
|
157 Dim two_k; |
|
158 }; |
|
159 |
|
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, |
|
163 const Graph& g) |
|
164 { return grid_force_pairs<Dim, PositionMap>(width, height, position, g); } |
|
165 |
|
166 template<typename Graph, typename PositionMap, typename Dim> |
|
167 void |
|
168 scale_graph(const Graph& g, PositionMap position, |
|
169 Dim left, Dim top, Dim right, Dim bottom) |
|
170 { |
|
171 if (num_vertices(g) == 0) return; |
|
172 |
|
173 if (bottom > top) { |
|
174 using std::swap; |
|
175 swap(bottom, top); |
|
176 } |
|
177 |
|
178 typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator; |
|
179 |
|
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); |
|
191 } |
|
192 |
|
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; |
|
199 } |
|
200 } |
|
201 |
|
202 namespace detail { |
|
203 template<typename PositionMap, typename DisplacementMap, |
|
204 typename RepulsiveForce, typename Dim, typename Graph> |
|
205 struct fr_apply_force |
|
206 { |
|
207 typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor; |
|
208 |
|
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) |
|
214 { } |
|
215 |
|
216 void operator()(vertex_descriptor u, vertex_descriptor v) |
|
217 { |
|
218 #ifndef BOOST_NO_STDC_NAMESPACE |
|
219 using std::sqrt; |
|
220 #endif // BOOST_NO_STDC_NAMESPACE |
|
221 if (u != v) { |
|
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; |
|
228 } |
|
229 } |
|
230 |
|
231 private: |
|
232 PositionMap position; |
|
233 DisplacementMap displacement; |
|
234 RepulsiveForce repulsive_force; |
|
235 Dim k; |
|
236 const Graph& g; |
|
237 }; |
|
238 |
|
239 } // end namespace detail |
|
240 |
|
241 template<typename Graph, typename PositionMap, typename Dim, |
|
242 typename AttractiveForce, typename RepulsiveForce, |
|
243 typename ForcePairs, typename Cooling, typename DisplacementMap> |
|
244 void |
|
245 fruchterman_reingold_force_directed_layout |
|
246 (const Graph& g, |
|
247 PositionMap position, |
|
248 Dim width, |
|
249 Dim height, |
|
250 AttractiveForce attractive_force, |
|
251 RepulsiveForce repulsive_force, |
|
252 ForcePairs force_pairs, |
|
253 Cooling cool, |
|
254 DisplacementMap displacement) |
|
255 { |
|
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; |
|
259 |
|
260 #ifndef BOOST_NO_STDC_NAMESPACE |
|
261 using std::sqrt; |
|
262 #endif // BOOST_NO_STDC_NAMESPACE |
|
263 |
|
264 Dim area = width * height; |
|
265 // assume positions are initialized randomly |
|
266 Dim k = sqrt(area / num_vertices(g)); |
|
267 |
|
268 detail::fr_apply_force<PositionMap, DisplacementMap, |
|
269 RepulsiveForce, Dim, Graph> |
|
270 apply_force(position, displacement, repulsive_force, k, g); |
|
271 |
|
272 Dim temp = cool(); |
|
273 if (temp) do { |
|
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; |
|
279 } |
|
280 force_pairs(g, apply_force); |
|
281 |
|
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); |
|
291 |
|
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; |
|
296 } |
|
297 |
|
298 // Update positions |
|
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 |
|
309 (width / 2, |
|
310 max BOOST_PREVENT_MACRO_SUBSTITUTION(-width / 2, |
|
311 position[*v].x)); |
|
312 position[*v].y = min BOOST_PREVENT_MACRO_SUBSTITUTION |
|
313 (height / 2, |
|
314 max BOOST_PREVENT_MACRO_SUBSTITUTION(-height / 2, |
|
315 position[*v].y)); |
|
316 } |
|
317 } while (temp = cool()); |
|
318 } |
|
319 |
|
320 namespace detail { |
|
321 template<typename DisplacementMap> |
|
322 struct fr_force_directed_layout |
|
323 { |
|
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> |
|
328 static void |
|
329 run(const Graph& g, |
|
330 PositionMap position, |
|
331 Dim width, |
|
332 Dim height, |
|
333 AttractiveForce attractive_force, |
|
334 RepulsiveForce repulsive_force, |
|
335 ForcePairs force_pairs, |
|
336 Cooling cool, |
|
337 DisplacementMap displacement, |
|
338 const bgl_named_params<Param, Tag, Rest>&) |
|
339 { |
|
340 fruchterman_reingold_force_directed_layout |
|
341 (g, position, width, height, attractive_force, repulsive_force, |
|
342 force_pairs, cool, displacement); |
|
343 } |
|
344 }; |
|
345 |
|
346 template<> |
|
347 struct fr_force_directed_layout<error_property_not_found> |
|
348 { |
|
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> |
|
353 static void |
|
354 run(const Graph& g, |
|
355 PositionMap position, |
|
356 Dim width, |
|
357 Dim height, |
|
358 AttractiveForce attractive_force, |
|
359 RepulsiveForce repulsive_force, |
|
360 ForcePairs force_pairs, |
|
361 Cooling cool, |
|
362 error_property_not_found, |
|
363 const bgl_named_params<Param, Tag, Rest>& params) |
|
364 { |
|
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, |
|
368 force_pairs, cool, |
|
369 make_iterator_property_map |
|
370 (displacements.begin(), |
|
371 choose_const_pmap(get_param(params, vertex_index), g, |
|
372 vertex_index), |
|
373 simple_point<Dim>())); |
|
374 } |
|
375 }; |
|
376 |
|
377 } // end namespace detail |
|
378 |
|
379 template<typename Graph, typename PositionMap, typename Dim, typename Param, |
|
380 typename Tag, typename Rest> |
|
381 void |
|
382 fruchterman_reingold_force_directed_layout |
|
383 (const Graph& g, |
|
384 PositionMap position, |
|
385 Dim width, |
|
386 Dim height, |
|
387 const bgl_named_params<Param, Tag, Rest>& params) |
|
388 { |
|
389 typedef typename property_value<bgl_named_params<Param,Tag,Rest>, |
|
390 vertex_displacement_t>::type D; |
|
391 |
|
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()), |
|
403 params); |
|
404 } |
|
405 |
|
406 template<typename Graph, typename PositionMap, typename Dim> |
|
407 void |
|
408 fruchterman_reingold_force_directed_layout(const Graph& g, |
|
409 PositionMap position, |
|
410 Dim width, |
|
411 Dim height) |
|
412 { |
|
413 fruchterman_reingold_force_directed_layout |
|
414 (g, position, width, height, |
|
415 attractive_force(square_distance_attractive_force())); |
|
416 } |
|
417 |
|
418 } // end namespace boost |
|
419 |
|
420 #endif // BOOST_GRAPH_FRUCHTERMAN_REINGOLD_FORCE_DIRECTED_LAYOUT_HPP |