williamr@2
|
1 |
// Copyright 2004 The Trustees of Indiana University.
|
williamr@2
|
2 |
|
williamr@2
|
3 |
// Distributed under the Boost Software License, Version 1.0.
|
williamr@2
|
4 |
// (See accompanying file LICENSE_1_0.txt or copy at
|
williamr@2
|
5 |
// http://www.boost.org/LICENSE_1_0.txt)
|
williamr@2
|
6 |
|
williamr@2
|
7 |
// Authors: Douglas Gregor
|
williamr@2
|
8 |
// Andrew Lumsdaine
|
williamr@2
|
9 |
#ifndef BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
|
williamr@2
|
10 |
#define BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
|
williamr@2
|
11 |
|
williamr@2
|
12 |
#include <boost/graph/graph_traits.hpp>
|
williamr@2
|
13 |
#include <boost/graph/johnson_all_pairs_shortest.hpp>
|
williamr@2
|
14 |
#include <boost/type_traits/is_convertible.hpp>
|
williamr@2
|
15 |
#include <utility>
|
williamr@2
|
16 |
#include <iterator>
|
williamr@2
|
17 |
#include <vector>
|
williamr@2
|
18 |
#include <boost/limits.hpp>
|
williamr@2
|
19 |
#include <cmath>
|
williamr@2
|
20 |
|
williamr@2
|
21 |
namespace boost {
|
williamr@2
|
22 |
namespace detail { namespace graph {
|
williamr@2
|
23 |
/**
|
williamr@2
|
24 |
* Denotes an edge or display area side length used to scale a
|
williamr@2
|
25 |
* Kamada-Kawai drawing.
|
williamr@2
|
26 |
*/
|
williamr@2
|
27 |
template<bool Edge, typename T>
|
williamr@2
|
28 |
struct edge_or_side
|
williamr@2
|
29 |
{
|
williamr@2
|
30 |
explicit edge_or_side(T value) : value(value) {}
|
williamr@2
|
31 |
|
williamr@2
|
32 |
T value;
|
williamr@2
|
33 |
};
|
williamr@2
|
34 |
|
williamr@2
|
35 |
/**
|
williamr@2
|
36 |
* Compute the edge length from an edge length. This is trivial.
|
williamr@2
|
37 |
*/
|
williamr@2
|
38 |
template<typename Graph, typename DistanceMap, typename IndexMap,
|
williamr@2
|
39 |
typename T>
|
williamr@2
|
40 |
T compute_edge_length(const Graph&, DistanceMap, IndexMap,
|
williamr@2
|
41 |
edge_or_side<true, T> length)
|
williamr@2
|
42 |
{ return length.value; }
|
williamr@2
|
43 |
|
williamr@2
|
44 |
/**
|
williamr@2
|
45 |
* Compute the edge length based on the display area side
|
williamr@2
|
46 |
length. We do this by dividing the side length by the largest
|
williamr@2
|
47 |
shortest distance between any two vertices in the graph.
|
williamr@2
|
48 |
*/
|
williamr@2
|
49 |
template<typename Graph, typename DistanceMap, typename IndexMap,
|
williamr@2
|
50 |
typename T>
|
williamr@2
|
51 |
T
|
williamr@2
|
52 |
compute_edge_length(const Graph& g, DistanceMap distance, IndexMap index,
|
williamr@2
|
53 |
edge_or_side<false, T> length)
|
williamr@2
|
54 |
{
|
williamr@2
|
55 |
T result(0);
|
williamr@2
|
56 |
|
williamr@2
|
57 |
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
|
williamr@2
|
58 |
|
williamr@2
|
59 |
for (vertex_iterator ui = vertices(g).first, end = vertices(g).second;
|
williamr@2
|
60 |
ui != end; ++ui) {
|
williamr@2
|
61 |
vertex_iterator vi = ui;
|
williamr@2
|
62 |
for (++vi; vi != end; ++vi) {
|
williamr@2
|
63 |
T dij = distance[get(index, *ui)][get(index, *vi)];
|
williamr@2
|
64 |
if (dij > result) result = dij;
|
williamr@2
|
65 |
}
|
williamr@2
|
66 |
}
|
williamr@2
|
67 |
return length.value / result;
|
williamr@2
|
68 |
}
|
williamr@2
|
69 |
|
williamr@2
|
70 |
/**
|
williamr@2
|
71 |
* Implementation of the Kamada-Kawai spring layout algorithm.
|
williamr@2
|
72 |
*/
|
williamr@2
|
73 |
template<typename Graph, typename PositionMap, typename WeightMap,
|
williamr@2
|
74 |
typename EdgeOrSideLength, typename Done,
|
williamr@2
|
75 |
typename VertexIndexMap, typename DistanceMatrix,
|
williamr@2
|
76 |
typename SpringStrengthMatrix, typename PartialDerivativeMap>
|
williamr@2
|
77 |
struct kamada_kawai_spring_layout_impl
|
williamr@2
|
78 |
{
|
williamr@2
|
79 |
typedef typename property_traits<WeightMap>::value_type weight_type;
|
williamr@2
|
80 |
typedef std::pair<weight_type, weight_type> deriv_type;
|
williamr@2
|
81 |
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
|
williamr@2
|
82 |
typedef typename graph_traits<Graph>::vertex_descriptor
|
williamr@2
|
83 |
vertex_descriptor;
|
williamr@2
|
84 |
|
williamr@2
|
85 |
kamada_kawai_spring_layout_impl(
|
williamr@2
|
86 |
const Graph& g,
|
williamr@2
|
87 |
PositionMap position,
|
williamr@2
|
88 |
WeightMap weight,
|
williamr@2
|
89 |
EdgeOrSideLength edge_or_side_length,
|
williamr@2
|
90 |
Done done,
|
williamr@2
|
91 |
weight_type spring_constant,
|
williamr@2
|
92 |
VertexIndexMap index,
|
williamr@2
|
93 |
DistanceMatrix distance,
|
williamr@2
|
94 |
SpringStrengthMatrix spring_strength,
|
williamr@2
|
95 |
PartialDerivativeMap partial_derivatives)
|
williamr@2
|
96 |
: g(g), position(position), weight(weight),
|
williamr@2
|
97 |
edge_or_side_length(edge_or_side_length), done(done),
|
williamr@2
|
98 |
spring_constant(spring_constant), index(index), distance(distance),
|
williamr@2
|
99 |
spring_strength(spring_strength),
|
williamr@2
|
100 |
partial_derivatives(partial_derivatives) {}
|
williamr@2
|
101 |
|
williamr@2
|
102 |
// Compute contribution of vertex i to the first partial
|
williamr@2
|
103 |
// derivatives (dE/dx_m, dE/dy_m) (for vertex m)
|
williamr@2
|
104 |
deriv_type
|
williamr@2
|
105 |
compute_partial_derivative(vertex_descriptor m, vertex_descriptor i)
|
williamr@2
|
106 |
{
|
williamr@2
|
107 |
#ifndef BOOST_NO_STDC_NAMESPACE
|
williamr@2
|
108 |
using std::sqrt;
|
williamr@2
|
109 |
#endif // BOOST_NO_STDC_NAMESPACE
|
williamr@2
|
110 |
|
williamr@2
|
111 |
deriv_type result(0, 0);
|
williamr@2
|
112 |
if (i != m) {
|
williamr@2
|
113 |
weight_type x_diff = position[m].x - position[i].x;
|
williamr@2
|
114 |
weight_type y_diff = position[m].y - position[i].y;
|
williamr@2
|
115 |
weight_type dist = sqrt(x_diff * x_diff + y_diff * y_diff);
|
williamr@2
|
116 |
result.first = spring_strength[get(index, m)][get(index, i)]
|
williamr@2
|
117 |
* (x_diff - distance[get(index, m)][get(index, i)]*x_diff/dist);
|
williamr@2
|
118 |
result.second = spring_strength[get(index, m)][get(index, i)]
|
williamr@2
|
119 |
* (y_diff - distance[get(index, m)][get(index, i)]*y_diff/dist);
|
williamr@2
|
120 |
}
|
williamr@2
|
121 |
|
williamr@2
|
122 |
return result;
|
williamr@2
|
123 |
}
|
williamr@2
|
124 |
|
williamr@2
|
125 |
// Compute partial derivatives dE/dx_m and dE/dy_m
|
williamr@2
|
126 |
deriv_type
|
williamr@2
|
127 |
compute_partial_derivatives(vertex_descriptor m)
|
williamr@2
|
128 |
{
|
williamr@2
|
129 |
#ifndef BOOST_NO_STDC_NAMESPACE
|
williamr@2
|
130 |
using std::sqrt;
|
williamr@2
|
131 |
#endif // BOOST_NO_STDC_NAMESPACE
|
williamr@2
|
132 |
|
williamr@2
|
133 |
deriv_type result(0, 0);
|
williamr@2
|
134 |
|
williamr@2
|
135 |
// TBD: looks like an accumulate to me
|
williamr@2
|
136 |
std::pair<vertex_iterator, vertex_iterator> verts = vertices(g);
|
williamr@2
|
137 |
for (/* no init */; verts.first != verts.second; ++verts.first) {
|
williamr@2
|
138 |
vertex_descriptor i = *verts.first;
|
williamr@2
|
139 |
deriv_type deriv = compute_partial_derivative(m, i);
|
williamr@2
|
140 |
result.first += deriv.first;
|
williamr@2
|
141 |
result.second += deriv.second;
|
williamr@2
|
142 |
}
|
williamr@2
|
143 |
|
williamr@2
|
144 |
return result;
|
williamr@2
|
145 |
}
|
williamr@2
|
146 |
|
williamr@2
|
147 |
// The actual Kamada-Kawai spring layout algorithm implementation
|
williamr@2
|
148 |
bool run()
|
williamr@2
|
149 |
{
|
williamr@2
|
150 |
#ifndef BOOST_NO_STDC_NAMESPACE
|
williamr@2
|
151 |
using std::sqrt;
|
williamr@2
|
152 |
#endif // BOOST_NO_STDC_NAMESPACE
|
williamr@2
|
153 |
|
williamr@2
|
154 |
// Compute d_{ij} and place it in the distance matrix
|
williamr@2
|
155 |
if (!johnson_all_pairs_shortest_paths(g, distance, index, weight,
|
williamr@2
|
156 |
weight_type(0)))
|
williamr@2
|
157 |
return false;
|
williamr@2
|
158 |
|
williamr@2
|
159 |
// Compute L based on side length (if needed), or retrieve L
|
williamr@2
|
160 |
weight_type edge_length =
|
williamr@2
|
161 |
detail::graph::compute_edge_length(g, distance, index,
|
williamr@2
|
162 |
edge_or_side_length);
|
williamr@2
|
163 |
|
williamr@2
|
164 |
// Compute l_{ij} and k_{ij}
|
williamr@2
|
165 |
const weight_type K = spring_constant;
|
williamr@2
|
166 |
vertex_iterator ui, end = vertices(g).second;
|
williamr@2
|
167 |
for (ui = vertices(g).first; ui != end; ++ui) {
|
williamr@2
|
168 |
vertex_iterator vi = ui;
|
williamr@2
|
169 |
for (++vi; vi != end; ++vi) {
|
williamr@2
|
170 |
weight_type dij = distance[get(index, *ui)][get(index, *vi)];
|
williamr@2
|
171 |
if (dij == (std::numeric_limits<weight_type>::max)())
|
williamr@2
|
172 |
return false;
|
williamr@2
|
173 |
distance[get(index, *ui)][get(index, *vi)] = edge_length * dij;
|
williamr@2
|
174 |
distance[get(index, *vi)][get(index, *ui)] = edge_length * dij;
|
williamr@2
|
175 |
spring_strength[get(index, *ui)][get(index, *vi)] = K/(dij*dij);
|
williamr@2
|
176 |
spring_strength[get(index, *vi)][get(index, *ui)] = K/(dij*dij);
|
williamr@2
|
177 |
}
|
williamr@2
|
178 |
}
|
williamr@2
|
179 |
|
williamr@2
|
180 |
// Compute Delta_i and find max
|
williamr@2
|
181 |
vertex_descriptor p = *vertices(g).first;
|
williamr@2
|
182 |
weight_type delta_p(0);
|
williamr@2
|
183 |
|
williamr@2
|
184 |
for (ui = vertices(g).first; ui != end; ++ui) {
|
williamr@2
|
185 |
deriv_type deriv = compute_partial_derivatives(*ui);
|
williamr@2
|
186 |
put(partial_derivatives, *ui, deriv);
|
williamr@2
|
187 |
|
williamr@2
|
188 |
weight_type delta =
|
williamr@2
|
189 |
sqrt(deriv.first*deriv.first + deriv.second*deriv.second);
|
williamr@2
|
190 |
|
williamr@2
|
191 |
if (delta > delta_p) {
|
williamr@2
|
192 |
p = *ui;
|
williamr@2
|
193 |
delta_p = delta;
|
williamr@2
|
194 |
}
|
williamr@2
|
195 |
}
|
williamr@2
|
196 |
|
williamr@2
|
197 |
while (!done(delta_p, p, g, true)) {
|
williamr@2
|
198 |
// The contribution p makes to the partial derivatives of
|
williamr@2
|
199 |
// each vertex. Computing this (at O(n) cost) allows us to
|
williamr@2
|
200 |
// update the delta_i values in O(n) time instead of O(n^2)
|
williamr@2
|
201 |
// time.
|
williamr@2
|
202 |
std::vector<deriv_type> p_partials(num_vertices(g));
|
williamr@2
|
203 |
for (ui = vertices(g).first; ui != end; ++ui) {
|
williamr@2
|
204 |
vertex_descriptor i = *ui;
|
williamr@2
|
205 |
p_partials[get(index, i)] = compute_partial_derivative(i, p);
|
williamr@2
|
206 |
}
|
williamr@2
|
207 |
|
williamr@2
|
208 |
do {
|
williamr@2
|
209 |
// Compute the 4 elements of the Jacobian
|
williamr@2
|
210 |
weight_type dE_dx_dx = 0, dE_dx_dy = 0, dE_dy_dx = 0, dE_dy_dy = 0;
|
williamr@2
|
211 |
for (ui = vertices(g).first; ui != end; ++ui) {
|
williamr@2
|
212 |
vertex_descriptor i = *ui;
|
williamr@2
|
213 |
if (i != p) {
|
williamr@2
|
214 |
weight_type x_diff = position[p].x - position[i].x;
|
williamr@2
|
215 |
weight_type y_diff = position[p].y - position[i].y;
|
williamr@2
|
216 |
weight_type dist = sqrt(x_diff * x_diff + y_diff * y_diff);
|
williamr@2
|
217 |
weight_type dist_cubed = dist * dist * dist;
|
williamr@2
|
218 |
weight_type k_mi = spring_strength[get(index,p)][get(index,i)];
|
williamr@2
|
219 |
weight_type l_mi = distance[get(index, p)][get(index, i)];
|
williamr@2
|
220 |
dE_dx_dx += k_mi * (1 - (l_mi * y_diff * y_diff)/dist_cubed);
|
williamr@2
|
221 |
dE_dx_dy += k_mi * l_mi * x_diff * y_diff / dist_cubed;
|
williamr@2
|
222 |
dE_dy_dx += k_mi * l_mi * x_diff * y_diff / dist_cubed;
|
williamr@2
|
223 |
dE_dy_dy += k_mi * (1 - (l_mi * x_diff * x_diff)/dist_cubed);
|
williamr@2
|
224 |
}
|
williamr@2
|
225 |
}
|
williamr@2
|
226 |
|
williamr@2
|
227 |
// Solve for delta_x and delta_y
|
williamr@2
|
228 |
weight_type dE_dx = get(partial_derivatives, p).first;
|
williamr@2
|
229 |
weight_type dE_dy = get(partial_derivatives, p).second;
|
williamr@2
|
230 |
|
williamr@2
|
231 |
weight_type delta_x =
|
williamr@2
|
232 |
(dE_dx_dy * dE_dy - dE_dy_dy * dE_dx)
|
williamr@2
|
233 |
/ (dE_dx_dx * dE_dy_dy - dE_dx_dy * dE_dy_dx);
|
williamr@2
|
234 |
|
williamr@2
|
235 |
weight_type delta_y =
|
williamr@2
|
236 |
(dE_dx_dx * dE_dy - dE_dy_dx * dE_dx)
|
williamr@2
|
237 |
/ (dE_dy_dx * dE_dx_dy - dE_dx_dx * dE_dy_dy);
|
williamr@2
|
238 |
|
williamr@2
|
239 |
|
williamr@2
|
240 |
// Move p by (delta_x, delta_y)
|
williamr@2
|
241 |
position[p].x += delta_x;
|
williamr@2
|
242 |
position[p].y += delta_y;
|
williamr@2
|
243 |
|
williamr@2
|
244 |
// Recompute partial derivatives and delta_p
|
williamr@2
|
245 |
deriv_type deriv = compute_partial_derivatives(p);
|
williamr@2
|
246 |
put(partial_derivatives, p, deriv);
|
williamr@2
|
247 |
|
williamr@2
|
248 |
delta_p =
|
williamr@2
|
249 |
sqrt(deriv.first*deriv.first + deriv.second*deriv.second);
|
williamr@2
|
250 |
} while (!done(delta_p, p, g, false));
|
williamr@2
|
251 |
|
williamr@2
|
252 |
// Select new p by updating each partial derivative and delta
|
williamr@2
|
253 |
vertex_descriptor old_p = p;
|
williamr@2
|
254 |
for (ui = vertices(g).first; ui != end; ++ui) {
|
williamr@2
|
255 |
deriv_type old_deriv_p = p_partials[get(index, *ui)];
|
williamr@2
|
256 |
deriv_type old_p_partial =
|
williamr@2
|
257 |
compute_partial_derivative(*ui, old_p);
|
williamr@2
|
258 |
deriv_type deriv = get(partial_derivatives, *ui);
|
williamr@2
|
259 |
|
williamr@2
|
260 |
deriv.first += old_p_partial.first - old_deriv_p.first;
|
williamr@2
|
261 |
deriv.second += old_p_partial.second - old_deriv_p.second;
|
williamr@2
|
262 |
|
williamr@2
|
263 |
put(partial_derivatives, *ui, deriv);
|
williamr@2
|
264 |
weight_type delta =
|
williamr@2
|
265 |
sqrt(deriv.first*deriv.first + deriv.second*deriv.second);
|
williamr@2
|
266 |
|
williamr@2
|
267 |
if (delta > delta_p) {
|
williamr@2
|
268 |
p = *ui;
|
williamr@2
|
269 |
delta_p = delta;
|
williamr@2
|
270 |
}
|
williamr@2
|
271 |
}
|
williamr@2
|
272 |
}
|
williamr@2
|
273 |
|
williamr@2
|
274 |
return true;
|
williamr@2
|
275 |
}
|
williamr@2
|
276 |
|
williamr@2
|
277 |
const Graph& g;
|
williamr@2
|
278 |
PositionMap position;
|
williamr@2
|
279 |
WeightMap weight;
|
williamr@2
|
280 |
EdgeOrSideLength edge_or_side_length;
|
williamr@2
|
281 |
Done done;
|
williamr@2
|
282 |
weight_type spring_constant;
|
williamr@2
|
283 |
VertexIndexMap index;
|
williamr@2
|
284 |
DistanceMatrix distance;
|
williamr@2
|
285 |
SpringStrengthMatrix spring_strength;
|
williamr@2
|
286 |
PartialDerivativeMap partial_derivatives;
|
williamr@2
|
287 |
};
|
williamr@2
|
288 |
} } // end namespace detail::graph
|
williamr@2
|
289 |
|
williamr@2
|
290 |
/// States that the given quantity is an edge length.
|
williamr@2
|
291 |
template<typename T>
|
williamr@2
|
292 |
inline detail::graph::edge_or_side<true, T>
|
williamr@2
|
293 |
edge_length(T x)
|
williamr@2
|
294 |
{ return detail::graph::edge_or_side<true, T>(x); }
|
williamr@2
|
295 |
|
williamr@2
|
296 |
/// States that the given quantity is a display area side length.
|
williamr@2
|
297 |
template<typename T>
|
williamr@2
|
298 |
inline detail::graph::edge_or_side<false, T>
|
williamr@2
|
299 |
side_length(T x)
|
williamr@2
|
300 |
{ return detail::graph::edge_or_side<false, T>(x); }
|
williamr@2
|
301 |
|
williamr@2
|
302 |
/**
|
williamr@2
|
303 |
* \brief Determines when to terminate layout of a particular graph based
|
williamr@2
|
304 |
* on a given relative tolerance.
|
williamr@2
|
305 |
*/
|
williamr@2
|
306 |
template<typename T = double>
|
williamr@2
|
307 |
struct layout_tolerance
|
williamr@2
|
308 |
{
|
williamr@2
|
309 |
layout_tolerance(const T& tolerance = T(0.001))
|
williamr@2
|
310 |
: tolerance(tolerance), last_energy((std::numeric_limits<T>::max)()),
|
williamr@2
|
311 |
last_local_energy((std::numeric_limits<T>::max)()) { }
|
williamr@2
|
312 |
|
williamr@2
|
313 |
template<typename Graph>
|
williamr@2
|
314 |
bool
|
williamr@2
|
315 |
operator()(T delta_p,
|
williamr@2
|
316 |
typename boost::graph_traits<Graph>::vertex_descriptor p,
|
williamr@2
|
317 |
const Graph& g,
|
williamr@2
|
318 |
bool global)
|
williamr@2
|
319 |
{
|
williamr@2
|
320 |
if (global) {
|
williamr@2
|
321 |
if (last_energy == (std::numeric_limits<T>::max)()) {
|
williamr@2
|
322 |
last_energy = delta_p;
|
williamr@2
|
323 |
return false;
|
williamr@2
|
324 |
}
|
williamr@2
|
325 |
|
williamr@2
|
326 |
T diff = last_energy - delta_p;
|
williamr@2
|
327 |
if (diff < T(0)) diff = -diff;
|
williamr@2
|
328 |
bool done = (delta_p == T(0) || diff / last_energy < tolerance);
|
williamr@2
|
329 |
last_energy = delta_p;
|
williamr@2
|
330 |
return done;
|
williamr@2
|
331 |
} else {
|
williamr@2
|
332 |
if (last_local_energy == (std::numeric_limits<T>::max)()) {
|
williamr@2
|
333 |
last_local_energy = delta_p;
|
williamr@2
|
334 |
return delta_p == T(0);
|
williamr@2
|
335 |
}
|
williamr@2
|
336 |
|
williamr@2
|
337 |
T diff = last_local_energy - delta_p;
|
williamr@2
|
338 |
bool done = (delta_p == T(0) || (diff / last_local_energy) < tolerance);
|
williamr@2
|
339 |
last_local_energy = delta_p;
|
williamr@2
|
340 |
return done;
|
williamr@2
|
341 |
}
|
williamr@2
|
342 |
}
|
williamr@2
|
343 |
|
williamr@2
|
344 |
private:
|
williamr@2
|
345 |
T tolerance;
|
williamr@2
|
346 |
T last_energy;
|
williamr@2
|
347 |
T last_local_energy;
|
williamr@2
|
348 |
};
|
williamr@2
|
349 |
|
williamr@2
|
350 |
/** \brief Kamada-Kawai spring layout for undirected graphs.
|
williamr@2
|
351 |
*
|
williamr@2
|
352 |
* This algorithm performs graph layout (in two dimensions) for
|
williamr@2
|
353 |
* connected, undirected graphs. It operates by relating the layout
|
williamr@2
|
354 |
* of graphs to a dynamic spring system and minimizing the energy
|
williamr@2
|
355 |
* within that system. The strength of a spring between two vertices
|
williamr@2
|
356 |
* is inversely proportional to the square of the shortest distance
|
williamr@2
|
357 |
* (in graph terms) between those two vertices. Essentially,
|
williamr@2
|
358 |
* vertices that are closer in the graph-theoretic sense (i.e., by
|
williamr@2
|
359 |
* following edges) will have stronger springs and will therefore be
|
williamr@2
|
360 |
* placed closer together.
|
williamr@2
|
361 |
*
|
williamr@2
|
362 |
* Prior to invoking this algorithm, it is recommended that the
|
williamr@2
|
363 |
* vertices be placed along the vertices of a regular n-sided
|
williamr@2
|
364 |
* polygon.
|
williamr@2
|
365 |
*
|
williamr@2
|
366 |
* \param g (IN) must be a model of Vertex List Graph, Edge List
|
williamr@2
|
367 |
* Graph, and Incidence Graph and must be undirected.
|
williamr@2
|
368 |
*
|
williamr@2
|
369 |
* \param position (OUT) must be a model of Lvalue Property Map,
|
williamr@2
|
370 |
* where the value type is a class containing fields @c x and @c y
|
williamr@2
|
371 |
* that will be set to the @c x and @c y coordinates of each vertex.
|
williamr@2
|
372 |
*
|
williamr@2
|
373 |
* \param weight (IN) must be a model of Readable Property Map,
|
williamr@2
|
374 |
* which provides the weight of each edge in the graph @p g.
|
williamr@2
|
375 |
*
|
williamr@2
|
376 |
* \param edge_or_side_length (IN) provides either the unit length
|
williamr@2
|
377 |
* @c e of an edge in the layout or the length of a side @c s of the
|
williamr@2
|
378 |
* display area, and must be either @c boost::edge_length(e) or @c
|
williamr@2
|
379 |
* boost::side_length(s), respectively.
|
williamr@2
|
380 |
*
|
williamr@2
|
381 |
* \param done (IN) is a 4-argument function object that is passed
|
williamr@2
|
382 |
* the current value of delta_p (i.e., the energy of vertex @p p),
|
williamr@2
|
383 |
* the vertex @p p, the graph @p g, and a boolean flag indicating
|
williamr@2
|
384 |
* whether @p delta_p is the maximum energy in the system (when @c
|
williamr@2
|
385 |
* true) or the energy of the vertex being moved. Defaults to @c
|
williamr@2
|
386 |
* layout_tolerance instantiated over the value type of the weight
|
williamr@2
|
387 |
* map.
|
williamr@2
|
388 |
*
|
williamr@2
|
389 |
* \param spring_constant (IN) is the constant multiplied by each
|
williamr@2
|
390 |
* spring's strength. Larger values create systems with more energy
|
williamr@2
|
391 |
* that can take longer to stabilize; smaller values create systems
|
williamr@2
|
392 |
* with less energy that stabilize quickly but do not necessarily
|
williamr@2
|
393 |
* result in pleasing layouts. The default value is 1.
|
williamr@2
|
394 |
*
|
williamr@2
|
395 |
* \param index (IN) is a mapping from vertices to index values
|
williamr@2
|
396 |
* between 0 and @c num_vertices(g). The default is @c
|
williamr@2
|
397 |
* get(vertex_index,g).
|
williamr@2
|
398 |
*
|
williamr@2
|
399 |
* \param distance (UTIL/OUT) will be used to store the distance
|
williamr@2
|
400 |
* from every vertex to every other vertex, which is computed in the
|
williamr@2
|
401 |
* first stages of the algorithm. This value's type must be a model
|
williamr@2
|
402 |
* of BasicMatrix with value type equal to the value type of the
|
williamr@2
|
403 |
* weight map. The default is a a vector of vectors.
|
williamr@2
|
404 |
*
|
williamr@2
|
405 |
* \param spring_strength (UTIL/OUT) will be used to store the
|
williamr@2
|
406 |
* strength of the spring between every pair of vertices. This
|
williamr@2
|
407 |
* value's type must be a model of BasicMatrix with value type equal
|
williamr@2
|
408 |
* to the value type of the weight map. The default is a a vector of
|
williamr@2
|
409 |
* vectors.
|
williamr@2
|
410 |
*
|
williamr@2
|
411 |
* \param partial_derivatives (UTIL) will be used to store the
|
williamr@2
|
412 |
* partial derivates of each vertex with respect to the @c x and @c
|
williamr@2
|
413 |
* y coordinates. This must be a Read/Write Property Map whose value
|
williamr@2
|
414 |
* type is a pair with both types equivalent to the value type of
|
williamr@2
|
415 |
* the weight map. The default is an iterator property map.
|
williamr@2
|
416 |
*
|
williamr@2
|
417 |
* \returns @c true if layout was successful or @c false if a
|
williamr@2
|
418 |
* negative weight cycle was detected.
|
williamr@2
|
419 |
*/
|
williamr@2
|
420 |
template<typename Graph, typename PositionMap, typename WeightMap,
|
williamr@2
|
421 |
typename T, bool EdgeOrSideLength, typename Done,
|
williamr@2
|
422 |
typename VertexIndexMap, typename DistanceMatrix,
|
williamr@2
|
423 |
typename SpringStrengthMatrix, typename PartialDerivativeMap>
|
williamr@2
|
424 |
bool
|
williamr@2
|
425 |
kamada_kawai_spring_layout(
|
williamr@2
|
426 |
const Graph& g,
|
williamr@2
|
427 |
PositionMap position,
|
williamr@2
|
428 |
WeightMap weight,
|
williamr@2
|
429 |
detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
williamr@2
|
430 |
Done done,
|
williamr@2
|
431 |
typename property_traits<WeightMap>::value_type spring_constant,
|
williamr@2
|
432 |
VertexIndexMap index,
|
williamr@2
|
433 |
DistanceMatrix distance,
|
williamr@2
|
434 |
SpringStrengthMatrix spring_strength,
|
williamr@2
|
435 |
PartialDerivativeMap partial_derivatives)
|
williamr@2
|
436 |
{
|
williamr@2
|
437 |
BOOST_STATIC_ASSERT((is_convertible<
|
williamr@2
|
438 |
typename graph_traits<Graph>::directed_category*,
|
williamr@2
|
439 |
undirected_tag*
|
williamr@2
|
440 |
>::value));
|
williamr@2
|
441 |
|
williamr@2
|
442 |
detail::graph::kamada_kawai_spring_layout_impl<
|
williamr@2
|
443 |
Graph, PositionMap, WeightMap,
|
williamr@2
|
444 |
detail::graph::edge_or_side<EdgeOrSideLength, T>, Done, VertexIndexMap,
|
williamr@2
|
445 |
DistanceMatrix, SpringStrengthMatrix, PartialDerivativeMap>
|
williamr@2
|
446 |
alg(g, position, weight, edge_or_side_length, done, spring_constant,
|
williamr@2
|
447 |
index, distance, spring_strength, partial_derivatives);
|
williamr@2
|
448 |
return alg.run();
|
williamr@2
|
449 |
}
|
williamr@2
|
450 |
|
williamr@2
|
451 |
/**
|
williamr@2
|
452 |
* \overload
|
williamr@2
|
453 |
*/
|
williamr@2
|
454 |
template<typename Graph, typename PositionMap, typename WeightMap,
|
williamr@2
|
455 |
typename T, bool EdgeOrSideLength, typename Done,
|
williamr@2
|
456 |
typename VertexIndexMap>
|
williamr@2
|
457 |
bool
|
williamr@2
|
458 |
kamada_kawai_spring_layout(
|
williamr@2
|
459 |
const Graph& g,
|
williamr@2
|
460 |
PositionMap position,
|
williamr@2
|
461 |
WeightMap weight,
|
williamr@2
|
462 |
detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
williamr@2
|
463 |
Done done,
|
williamr@2
|
464 |
typename property_traits<WeightMap>::value_type spring_constant,
|
williamr@2
|
465 |
VertexIndexMap index)
|
williamr@2
|
466 |
{
|
williamr@2
|
467 |
typedef typename property_traits<WeightMap>::value_type weight_type;
|
williamr@2
|
468 |
|
williamr@2
|
469 |
typename graph_traits<Graph>::vertices_size_type n = num_vertices(g);
|
williamr@2
|
470 |
typedef std::vector<weight_type> weight_vec;
|
williamr@2
|
471 |
|
williamr@2
|
472 |
std::vector<weight_vec> distance(n, weight_vec(n));
|
williamr@2
|
473 |
std::vector<weight_vec> spring_strength(n, weight_vec(n));
|
williamr@2
|
474 |
std::vector<std::pair<weight_type, weight_type> > partial_derivatives(n);
|
williamr@2
|
475 |
|
williamr@2
|
476 |
return
|
williamr@2
|
477 |
kamada_kawai_spring_layout(
|
williamr@2
|
478 |
g, position, weight, edge_or_side_length, done, spring_constant, index,
|
williamr@2
|
479 |
distance.begin(),
|
williamr@2
|
480 |
spring_strength.begin(),
|
williamr@2
|
481 |
make_iterator_property_map(partial_derivatives.begin(), index,
|
williamr@2
|
482 |
std::pair<weight_type, weight_type>()));
|
williamr@2
|
483 |
}
|
williamr@2
|
484 |
|
williamr@2
|
485 |
/**
|
williamr@2
|
486 |
* \overload
|
williamr@2
|
487 |
*/
|
williamr@2
|
488 |
template<typename Graph, typename PositionMap, typename WeightMap,
|
williamr@2
|
489 |
typename T, bool EdgeOrSideLength, typename Done>
|
williamr@2
|
490 |
bool
|
williamr@2
|
491 |
kamada_kawai_spring_layout(
|
williamr@2
|
492 |
const Graph& g,
|
williamr@2
|
493 |
PositionMap position,
|
williamr@2
|
494 |
WeightMap weight,
|
williamr@2
|
495 |
detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
williamr@2
|
496 |
Done done,
|
williamr@2
|
497 |
typename property_traits<WeightMap>::value_type spring_constant)
|
williamr@2
|
498 |
{
|
williamr@2
|
499 |
return kamada_kawai_spring_layout(g, position, weight, edge_or_side_length,
|
williamr@2
|
500 |
done, spring_constant,
|
williamr@2
|
501 |
get(vertex_index, g));
|
williamr@2
|
502 |
}
|
williamr@2
|
503 |
|
williamr@2
|
504 |
/**
|
williamr@2
|
505 |
* \overload
|
williamr@2
|
506 |
*/
|
williamr@2
|
507 |
template<typename Graph, typename PositionMap, typename WeightMap,
|
williamr@2
|
508 |
typename T, bool EdgeOrSideLength, typename Done>
|
williamr@2
|
509 |
bool
|
williamr@2
|
510 |
kamada_kawai_spring_layout(
|
williamr@2
|
511 |
const Graph& g,
|
williamr@2
|
512 |
PositionMap position,
|
williamr@2
|
513 |
WeightMap weight,
|
williamr@2
|
514 |
detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length,
|
williamr@2
|
515 |
Done done)
|
williamr@2
|
516 |
{
|
williamr@2
|
517 |
typedef typename property_traits<WeightMap>::value_type weight_type;
|
williamr@2
|
518 |
return kamada_kawai_spring_layout(g, position, weight, edge_or_side_length,
|
williamr@2
|
519 |
done, weight_type(1));
|
williamr@2
|
520 |
}
|
williamr@2
|
521 |
|
williamr@2
|
522 |
/**
|
williamr@2
|
523 |
* \overload
|
williamr@2
|
524 |
*/
|
williamr@2
|
525 |
template<typename Graph, typename PositionMap, typename WeightMap,
|
williamr@2
|
526 |
typename T, bool EdgeOrSideLength>
|
williamr@2
|
527 |
bool
|
williamr@2
|
528 |
kamada_kawai_spring_layout(
|
williamr@2
|
529 |
const Graph& g,
|
williamr@2
|
530 |
PositionMap position,
|
williamr@2
|
531 |
WeightMap weight,
|
williamr@2
|
532 |
detail::graph::edge_or_side<EdgeOrSideLength, T> edge_or_side_length)
|
williamr@2
|
533 |
{
|
williamr@2
|
534 |
typedef typename property_traits<WeightMap>::value_type weight_type;
|
williamr@2
|
535 |
return kamada_kawai_spring_layout(g, position, weight, edge_or_side_length,
|
williamr@2
|
536 |
layout_tolerance<weight_type>(),
|
williamr@2
|
537 |
weight_type(1.0),
|
williamr@2
|
538 |
get(vertex_index, g));
|
williamr@2
|
539 |
}
|
williamr@2
|
540 |
} // end namespace boost
|
williamr@2
|
541 |
|
williamr@2
|
542 |
#endif // BOOST_GRAPH_KAMADA_KAWAI_SPRING_LAYOUT_HPP
|