algorithm.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. /*
  2. * algorithm.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "algorithm.h"
  12. double Algorithm::distance(double x1, double y1, double x2, double y2)
  13. {
  14. return std::sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) + 1e-9;
  15. }
  16. bool Algorithm::edgesIntersect(const Node& a, const Node& b, const Node& c, const Node& d)
  17. {
  18. auto cross = [](double x1, double y1, double x2, double y2) {
  19. return x1 * y2 - y1 * x2;
  20. };
  21. double dx1 = b.x - a.x, dy1 = b.y - a.y;
  22. double dx2 = d.x - c.x, dy2 = d.y - c.y;
  23. double delta = cross(dx1, dy1, dx2, dy2);
  24. if (std::abs(delta) < 1e-10) return false; // Parallel
  25. // Compute intersection
  26. double s = cross(c.x - a.x, c.y - a.y, dx2, dy2) / delta;
  27. double t = cross(c.x - a.x, c.y - a.y, dx1, dy1) / delta;
  28. return s > 0 && s < 1 && t > 0 && t < 1;
  29. }
  30. void Algorithm::forceDirectedLayout(std::vector<Node> & nodes, const std::vector<Edge> & edges, int iterations, double width, double height)
  31. {
  32. const double area = width * height;
  33. const double k = std::sqrt(area / nodes.size());
  34. for (int it = 0; it < iterations; ++it)
  35. {
  36. // Reset forces
  37. for (auto& node : nodes)
  38. node.dx = node.dy = 0;
  39. // Repulsive forces
  40. for (size_t i = 0; i < nodes.size(); ++i)
  41. {
  42. for (size_t j = i + 1; j < nodes.size(); ++j)
  43. {
  44. double dx = nodes[i].x - nodes[j].x;
  45. double dy = nodes[i].y - nodes[j].y;
  46. double dist = distance(nodes[i].x, nodes[i].y, nodes[j].x, nodes[j].y);
  47. double force = (k * k) / dist;
  48. nodes[i].dx += (dx / dist) * force;
  49. nodes[i].dy += (dy / dist) * force;
  50. nodes[j].dx -= (dx / dist) * force;
  51. nodes[j].dy -= (dy / dist) * force;
  52. }
  53. }
  54. // Attractive forces
  55. for (const auto& edge : edges)
  56. {
  57. Node& u = nodes[edge.from];
  58. Node& v = nodes[edge.to];
  59. double dx = u.x - v.x;
  60. double dy = u.y - v.y;
  61. double dist = distance(u.x, u.y, v.x, v.y);
  62. double force = (dist * dist) / k;
  63. double fx = (dx / dist) * force;
  64. double fy = (dy / dist) * force;
  65. u.dx -= fx;
  66. u.dy -= fy;
  67. v.dx += fx;
  68. v.dy += fy;
  69. }
  70. // Edge crossing penalty
  71. for (size_t i = 0; i < edges.size(); ++i) {
  72. for (size_t j = i + 1; j < edges.size(); ++j) {
  73. const Edge& e1 = edges[i];
  74. const Edge& e2 = edges[j];
  75. if (e1.from == e2.from || e1.from == e2.to ||
  76. e1.to == e2.from || e1.to == e2.to)
  77. continue; // Skip if they share nodes
  78. Node& a = nodes[e1.from];
  79. Node& b = nodes[e1.to];
  80. Node& c = nodes[e2.from];
  81. Node& d = nodes[e2.to];
  82. if (edgesIntersect(a, b, c, d)) {
  83. double strength = 0.05;
  84. a.dx += strength * (a.x - c.x);
  85. a.dy += strength * (a.y - c.y);
  86. b.dx += strength * (b.x - d.x);
  87. b.dy += strength * (b.y - d.y);
  88. c.dx += strength * (c.x - a.x);
  89. c.dy += strength * (c.y - a.y);
  90. d.dx += strength * (d.x - b.x);
  91. d.dy += strength * (d.y - b.y);
  92. }
  93. }
  94. }
  95. // Apply displacement
  96. for (auto& node : nodes)
  97. {
  98. node.x += std::max(-5.0, std::min(5.0, node.dx));
  99. node.y += std::max(-5.0, std::min(5.0, node.dy));
  100. // Keep within bounds
  101. node.x = std::min(width, std::max(0.0, node.x));
  102. node.y = std::min(height, std::max(0.0, node.y));
  103. }
  104. }
  105. for (auto& node : nodes)
  106. {
  107. // Center around 0
  108. node.x -= width / 2;
  109. node.y -= height / 2;
  110. }
  111. }