18 #include "optimization_grid.h"
23 #include <unordered_map>
38 template<
typename InitialLayout>
41 using disp_map = std::unordered_map<node_id, coords>;
53 template<
typename Graph>
59 m_initial_layouter(
graph, canvas_width, canvas_height);
60 layout_pass(canvas_width, canvas_height,
graph, m_first_cooling);
61 layout_pass(canvas_width, canvas_height,
graph, m_second_cooling);
72 return length(width, height) * UnitCoeff;
77 m_first_cooling = std::move(c);
82 m_second_cooling = std::move(c);
100 m_border_force = coeff;
113 m_use_global_repulsion = value;
127 template<
typename Graph>
129 float area = width * height;
130 float k = m_k_coeff * std::sqrt(area /
static_cast<float>(
graph.
nodes().size()));
133 std::vector<coords> displacements(
graph.
nodes().size());
134 reset_and_border(
graph, width, height, k, displacements);
135 repulsive_forces(
graph, width, height, k, temperature, displacements);
136 attractive_forces(
graph, k, displacements);
137 displacement(
graph, width, height, temperature, displacements);
141 static constexpr
float SmallOffset = 0.001f;
142 static constexpr
float UnitCoeff = 0.68;
144 float m_border_force = 0.6;
145 float m_k_coeff = 0.6;
146 bool m_use_global_repulsion =
false;
148 cooling m_first_cooling{ 500, 0.8, [](
float t){
return t * 0.9893; } };
149 cooling m_second_cooling{ 500, 0.05, [](
float t){
return t * 0.993; } };
151 InitialLayout m_initial_layouter;
153 template<
typename Graph>
154 void reset_and_border(
159 , std::vector<coords>& disp)
const {
161 for (
unsigned i = 0; i < graph.nodes().size(); ++i) {
162 auto& pos = graph.nodes()[i].pos();
163 disp[i].x = border_displacement(k, width, pos.x);
164 disp[i].y = border_displacement(k, height, pos.y);
168 template<
typename Graph>
169 void repulsive_forces(
175 , std::vector<coords>& disp)
const {
176 std::mt19937 rand_gen(0);
177 std::uniform_real_distribution<float> rand_angle(0.0f, 3.14159f * 2.0f);
180 for_each_pair_of_nodes(graph, width, height, k, [&](
unsigned i,
unsigned j){
181 auto& node_i = graph.nodes()[i];
182 auto& node_j = graph.nodes()[j];
183 float diff_x = node_j.pos().x - node_i.pos().x;
184 float diff_y = node_j.pos().y - node_i.pos().y;
185 float dst = length(diff_x, diff_y);
189 float angle = rand_angle(rand_gen);
191 disp[i].x -= std::cos(angle) * r;
192 disp[i].y -= std::sin(angle) * r;
193 disp[j].x += std::cos(angle) * r;
194 disp[j].y += std::sin(angle) * r;
195 }
else if (m_use_global_repulsion || dst < k * 2.0f) {
196 float rep_force = (1.0f / dst) * (k * k / dst);
197 disp[i].x -= diff_x * rep_force;
198 disp[i].y -= diff_y * rep_force;
199 disp[j].x += diff_x * rep_force;
200 disp[j].y += diff_y * rep_force;
206 template <
typename Graph,
typename Function>
207 void for_each_pair_of_nodes(
212 , Function func)
const {
213 if (m_use_global_repulsion) {
214 for (
unsigned i = 0; i < graph.nodes().size(); ++i) {
215 for (
unsigned j = 0; j < i; ++j) {
221 detail::optimization_grid m_grid(width, height, k);
222 for (
unsigned i = 0; i < graph.nodes().size(); ++i) {
223 m_grid.add(graph.nodes()[i].pos(), i);
226 for (
unsigned i = 0; i < graph.nodes().size(); ++i) {
227 auto& node_i = graph.nodes()[i];
228 m_grid.for_each_around(node_i.pos(), [&](
unsigned j){
237 template<
typename Graph>
238 void attractive_forces(Graph& graph,
float k, std::vector<coords>& disp)
const {
239 for (
const auto& e : graph.edges()) {
240 unsigned index_one = graph.node_index(e.one_id());
241 unsigned index_two = graph.node_index(e.two_id());
242 auto& node_one = graph.nodes()[index_one];
243 auto& node_two = graph.nodes()[index_two];
244 float diff_x = node_two.pos().x - node_one.pos().x;
245 float diff_y = node_two.pos().y - node_one.pos().y;
246 float dst = length(diff_x, diff_y);
248 auto& disp_one = disp[index_one];
249 auto& disp_two = disp[index_two];
250 float attr_force = (1.0f / dst) * (dst * dst / k);
251 disp_one.x += diff_x * attr_force;
252 disp_one.y += diff_y * attr_force;
253 disp_two.x -= diff_x * attr_force;
254 disp_two.y -= diff_y * attr_force;
259 template<
typename Graph>
265 ,
const std::vector<coords>& disp)
const {
266 for (
unsigned i = 0; i < graph.nodes().size(); ++i) {
267 auto& node = graph.nodes()[i];
268 const auto& current_disp = disp[i];
269 float disp_len = length(current_disp.x, current_disp.y);
271 float result_disp = std::min(disp_len, t) / disp_len;
272 node.pos().x += result_disp * current_disp.x;
273 node.pos().y += result_disp * current_disp.y;
275 auto clamp = [](
float size,
float coord){
276 return std::min(size, std::max(-size, coord));
278 node.pos().x = clamp(width * 0.5f, node.pos().x);
279 node.pos().y = clamp(height * 0.5f, node.pos().y);
283 float border_displacement(
float k,
float coord,
float size)
const {
284 auto displace = [&](
float coord,
float size){
285 return (k * k * m_border_force) / (std::fabs(size * 0.5f - coord)
286 + std::fabs(size * SmallOffset));
288 return displace(coord, -size)
289 - displace(coord, size);
292 template<
typename Graph>
297 ,
const cooling& c) {
298 float t = c.start_temperature;
299 for (
unsigned r = 0; r < c.iterations; ++r) {
300 iteration(graph, width, height, t);
305 float pow2(
float one)
const {
310 float length(
float a,
float b)
const {
311 return std::sqrt(pow2(a) + pow2(b));