layout_test.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. // Copyright 2004 The Trustees of Indiana University.
  2. // Use, modification and distribution is subject to the Boost Software
  3. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. // Authors: Douglas Gregor
  6. // Andrew Lumsdaine
  7. #include <boost/config.hpp>
  8. #ifdef BOOST_MSVC
  9. // Without disabling this we get hard errors about initialialized pointers:
  10. #pragma warning(disable:4703)
  11. #endif
  12. #include <boost/graph/fruchterman_reingold.hpp>
  13. #include <boost/graph/random_layout.hpp>
  14. #include <boost/graph/kamada_kawai_spring_layout.hpp>
  15. #include <boost/graph/circle_layout.hpp>
  16. #include <boost/graph/adjacency_list.hpp>
  17. #include <boost/graph/point_traits.hpp>
  18. #include <boost/random/linear_congruential.hpp>
  19. #include <boost/test/minimal.hpp>
  20. #include <iostream>
  21. #include <boost/limits.hpp>
  22. #include <fstream>
  23. #include <string>
  24. using namespace boost;
  25. enum vertex_position_t { vertex_position };
  26. namespace boost { BOOST_INSTALL_PROPERTY(vertex, position); }
  27. typedef square_topology<>::point_type point;
  28. template<typename Graph, typename PositionMap, typename Topology>
  29. void print_graph_layout(const Graph& g, PositionMap position, const Topology& topology)
  30. {
  31. typedef typename Topology::point_type Point;
  32. // Find min/max ranges
  33. Point min_point = position[*vertices(g).first], max_point = min_point;
  34. BGL_FORALL_VERTICES_T(v, g, Graph) {
  35. min_point = topology.pointwise_min(min_point, position[v]);
  36. max_point = topology.pointwise_max(max_point, position[v]);
  37. }
  38. for (int y = (int)min_point[1]; y <= (int)max_point[1]; ++y) {
  39. for (int x = (int)min_point[0]; x <= (int)max_point[0]; ++x) {
  40. typename graph_traits<Graph>::vertex_iterator vi, vi_end;
  41. // Find vertex at this position
  42. typename graph_traits<Graph>::vertices_size_type index = 0;
  43. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi, ++index) {
  44. if ((int)position[*vi][0] == x && (int)position[*vi][1] == y)
  45. break;
  46. }
  47. if (vi == vi_end) std::cout << ' ';
  48. else std::cout << (char)(index + 'A');
  49. }
  50. std::cout << std::endl;
  51. }
  52. }
  53. template<typename Graph, typename PositionMap>
  54. void dump_graph_layout(std::string name, const Graph& g, PositionMap position)
  55. {
  56. std::ofstream out((name + ".dot").c_str());
  57. out << "graph " << name << " {" << std::endl;
  58. typename graph_traits<Graph>::vertex_iterator vi, vi_end;
  59. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
  60. out << " n" << get(vertex_index, g, *vi) << "[ pos=\""
  61. << (int)position[*vi][0] + 25 << ", " << (int)position[*vi][1] + 25
  62. << "\" ];\n";
  63. }
  64. typename graph_traits<Graph>::edge_iterator ei, ei_end;
  65. for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
  66. out << " n" << get(vertex_index, g, source(*ei, g)) << " -- n"
  67. << get(vertex_index, g, target(*ei, g)) << ";\n";
  68. }
  69. out << "}\n";
  70. }
  71. template<typename Graph>
  72. void
  73. test_circle_layout(Graph*, typename graph_traits<Graph>::vertices_size_type n)
  74. {
  75. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  76. typedef typename graph_traits<Graph>::vertices_size_type vertices_size_type;
  77. Graph g(n);
  78. // Initialize vertex indices
  79. vertex_iterator vi = vertices(g).first;
  80. for (vertices_size_type i = 0; i < n; ++i, ++vi)
  81. put(vertex_index, g, *vi, i);
  82. circle_graph_layout(g, get(vertex_position, g), 10.0);
  83. std::cout << "Regular polygon layout with " << n << " points.\n";
  84. square_topology<> topology;
  85. print_graph_layout(g, get(vertex_position, g), topology);
  86. }
  87. struct simple_edge
  88. {
  89. int first, second;
  90. };
  91. struct kamada_kawai_done
  92. {
  93. kamada_kawai_done() : last_delta() {}
  94. template<typename Graph>
  95. bool operator()(double delta_p,
  96. typename boost::graph_traits<Graph>::vertex_descriptor /*p*/,
  97. const Graph& /*g*/,
  98. bool global)
  99. {
  100. if (global) {
  101. double diff = last_delta - delta_p;
  102. if (diff < 0) diff = -diff;
  103. last_delta = delta_p;
  104. return diff < 0.01;
  105. } else {
  106. return delta_p < 0.01;
  107. }
  108. }
  109. double last_delta;
  110. };
  111. template<typename Graph>
  112. void
  113. test_triangle(Graph*)
  114. {
  115. typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor;
  116. typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
  117. Graph g;
  118. vertex_descriptor u = add_vertex(g); put(vertex_index, g, u, 0);
  119. vertex_descriptor v = add_vertex(g); put(vertex_index, g, v, 1);
  120. vertex_descriptor w = add_vertex(g); put(vertex_index, g, w, 2);
  121. edge_descriptor e1 = add_edge(u, v, g).first; put(edge_weight, g, e1, 1.0);
  122. edge_descriptor e2 = add_edge(v, w, g).first; put(edge_weight, g, e2, 1.0);
  123. edge_descriptor e3 = add_edge(w, u, g).first; put(edge_weight, g, e3, 1.0);
  124. circle_graph_layout(g, get(vertex_position, g), 25.0);
  125. bool ok = kamada_kawai_spring_layout(g,
  126. get(vertex_position, g),
  127. get(edge_weight, g),
  128. square_topology<>(50.0),
  129. side_length(50.0));
  130. BOOST_CHECK(ok);
  131. std::cout << "Triangle layout (Kamada-Kawai).\n";
  132. print_graph_layout(g, get(vertex_position, g));
  133. }
  134. template<typename Graph>
  135. void
  136. test_cube(Graph*)
  137. {
  138. enum {A, B, C, D, E, F, G, H};
  139. simple_edge cube_edges[12] = {
  140. {A, E}, {A, B}, {A, D}, {B, F}, {B, C}, {C, D}, {C, G}, {D, H},
  141. {E, H}, {E, F}, {F, G}, {G, H}
  142. };
  143. Graph g(&cube_edges[0], &cube_edges[12], 8);
  144. typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
  145. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  146. vertex_iterator vi, vi_end;
  147. int i = 0;
  148. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
  149. put(vertex_index, g, *vi, i++);
  150. edge_iterator ei, ei_end;
  151. for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
  152. put(edge_weight, g, *ei, 1.0);
  153. std::cerr << "(" << (char)(get(vertex_index, g, source(*ei, g)) + 'A')
  154. << ", " << (char)(get(vertex_index, g, target(*ei, g)) + 'A')
  155. << ") ";
  156. }
  157. std::cerr << std::endl;
  158. circle_graph_layout(g, get(vertex_position, g), 25.0);
  159. bool ok = kamada_kawai_spring_layout(g,
  160. get(vertex_position, g),
  161. get(edge_weight, g),
  162. square_topology<>(50.0),
  163. side_length(50.0),
  164. kamada_kawai_done());
  165. BOOST_CHECK(ok);
  166. std::cout << "Cube layout (Kamada-Kawai).\n";
  167. print_graph_layout(g, get(vertex_position, g), square_topology<>(50.));
  168. dump_graph_layout("cube", g, get(vertex_position, g));
  169. minstd_rand gen;
  170. typedef square_topology<> Topology;
  171. Topology topology(gen, 50.0);
  172. std::vector<Topology::point_difference_type> displacements(num_vertices(g));
  173. rectangle_topology<> rect_top(gen, 0, 0, 50, 50);
  174. random_graph_layout(g, get(vertex_position, g), rect_top);
  175. fruchterman_reingold_force_directed_layout
  176. (g,
  177. get(vertex_position, g),
  178. topology,
  179. square_distance_attractive_force(),
  180. square_distance_repulsive_force(),
  181. all_force_pairs(),
  182. linear_cooling<double>(100),
  183. make_iterator_property_map(displacements.begin(),
  184. get(vertex_index, g),
  185. Topology::point_difference_type()));
  186. std::cout << "Cube layout (Fruchterman-Reingold).\n";
  187. print_graph_layout(g, get(vertex_position, g), square_topology<>(50.));
  188. dump_graph_layout("cube-fr", g, get(vertex_position, g));
  189. }
  190. template<typename Graph>
  191. void
  192. test_triangular(Graph*)
  193. {
  194. enum {A, B, C, D, E, F, G, H, I, J};
  195. simple_edge triangular_edges[18] = {
  196. {A, B}, {A, C}, {B, C}, {B, D}, {B, E}, {C, E}, {C, F}, {D, E}, {D, G},
  197. {D, H}, {E, F}, {E, H}, {E, I}, {F, I}, {F, J}, {G, H}, {H, I}, {I, J}
  198. };
  199. Graph g(&triangular_edges[0], &triangular_edges[18], 10);
  200. typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
  201. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  202. vertex_iterator vi, vi_end;
  203. int i = 0;
  204. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
  205. put(vertex_index, g, *vi, i++);
  206. edge_iterator ei, ei_end;
  207. for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
  208. put(edge_weight, g, *ei, 1.0);
  209. std::cerr << "(" << (char)(get(vertex_index, g, source(*ei, g)) + 'A')
  210. << ", " << (char)(get(vertex_index, g, target(*ei, g)) + 'A')
  211. << ") ";
  212. }
  213. std::cerr << std::endl;
  214. typedef square_topology<> Topology;
  215. minstd_rand gen;
  216. Topology topology(gen, 50.0);
  217. Topology::point_type origin;
  218. origin[0] = origin[1] = 50.0;
  219. Topology::point_difference_type extent;
  220. extent[0] = extent[1] = 50.0;
  221. circle_graph_layout(g, get(vertex_position, g), 25.0);
  222. bool ok = kamada_kawai_spring_layout(g,
  223. get(vertex_position, g),
  224. get(edge_weight, g),
  225. topology,
  226. side_length(50.0),
  227. kamada_kawai_done());
  228. BOOST_CHECK(ok);
  229. std::cout << "Triangular layout (Kamada-Kawai).\n";
  230. print_graph_layout(g, get(vertex_position, g), square_topology<>(50.));
  231. dump_graph_layout("triangular-kk", g, get(vertex_position, g));
  232. rectangle_topology<> rect_top(gen, -25, -25, 25, 25);
  233. random_graph_layout(g, get(vertex_position, g), rect_top);
  234. dump_graph_layout("random", g, get(vertex_position, g));
  235. std::vector<Topology::point_difference_type> displacements(num_vertices(g));
  236. fruchterman_reingold_force_directed_layout
  237. (g,
  238. get(vertex_position, g),
  239. topology,
  240. attractive_force(square_distance_attractive_force()).
  241. cooling(linear_cooling<double>(100)));
  242. std::cout << "Triangular layout (Fruchterman-Reingold).\n";
  243. print_graph_layout(g, get(vertex_position, g), square_topology<>(50.));
  244. dump_graph_layout("triangular-fr", g, get(vertex_position, g));
  245. }
  246. template<typename Graph>
  247. void
  248. test_disconnected(Graph*)
  249. {
  250. enum {A, B, C, D, E, F, G, H};
  251. simple_edge triangular_edges[13] = {
  252. {A, B}, {B, C}, {C, A},
  253. {D, E}, {E, F}, {F, G}, {G, H}, {H, D},
  254. {D, F}, {F, H}, {H, E}, {E, G}, {G, D}
  255. };
  256. Graph g(&triangular_edges[0], &triangular_edges[13], 8);
  257. typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
  258. typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
  259. vertex_iterator vi, vi_end;
  260. int i = 0;
  261. for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
  262. put(vertex_index, g, *vi, i++);
  263. edge_iterator ei, ei_end;
  264. for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
  265. put(edge_weight, g, *ei, 1.0);
  266. std::cerr << "(" << (char)(get(vertex_index, g, source(*ei, g)) + 'A')
  267. << ", " << (char)(get(vertex_index, g, target(*ei, g)) + 'A')
  268. << ") ";
  269. }
  270. std::cerr << std::endl;
  271. circle_graph_layout(g, get(vertex_position, g), 25.0);
  272. bool ok = kamada_kawai_spring_layout(g,
  273. get(vertex_position, g),
  274. get(edge_weight, g),
  275. square_topology<>(50.0),
  276. side_length(50.0),
  277. kamada_kawai_done());
  278. BOOST_CHECK(!ok);
  279. minstd_rand gen;
  280. rectangle_topology<> rect_top(gen, -25, -25, 25, 25);
  281. random_graph_layout(g, get(vertex_position, g), rect_top);
  282. typedef square_topology<> Topology;
  283. Topology topology(gen, 50.0);
  284. std::vector<Topology::point_difference_type> displacements(num_vertices(g));
  285. fruchterman_reingold_force_directed_layout
  286. (g,
  287. get(vertex_position, g),
  288. topology,
  289. attractive_force(square_distance_attractive_force()).
  290. cooling(linear_cooling<double>(50)));
  291. std::cout << "Disconnected layout (Fruchterman-Reingold).\n";
  292. print_graph_layout(g, get(vertex_position, g), square_topology<>(50.));
  293. dump_graph_layout("disconnected-fr", g, get(vertex_position, g));
  294. }
  295. int test_main(int, char*[])
  296. {
  297. typedef adjacency_list<listS, listS, undirectedS,
  298. // Vertex properties
  299. property<vertex_index_t, int,
  300. property<vertex_position_t, point> >,
  301. // Edge properties
  302. property<edge_weight_t, double> > Graph;
  303. test_circle_layout((Graph*)0, 5);
  304. test_cube((Graph*)0);
  305. test_triangular((Graph*)0);
  306. test_disconnected((Graph*)0);
  307. return 0;
  308. }