1 /** \file
2 * \brief Implementation of Fast Multipole Multilevel Method (FM^3).
3 *
4 * \author Stefan Hachul
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32
33 #include <ogdf/energybased/FMMMLayout.h>
34 #include <ogdf/energybased/fmmm/numexcept.h>
35 #include <ogdf/energybased/fmmm/MAARPacking.h>
36 #include <ogdf/energybased/fmmm/Multilevel.h>
37 #include <ogdf/basic/simple_graph_alg.h>
38
39 namespace ogdf {
40
41 using energybased::fmmm::numexcept;
42
FMMMLayout()43 FMMMLayout::FMMMLayout()
44 {
45 initialize_all_options();
46 }
47
48
call(GraphAttributes & GA)49 void FMMMLayout::call(GraphAttributes &GA)
50 {
51 const Graph &G = GA.constGraph();
52 EdgeArray<double> edgelength(G,1.0);
53 call(GA,edgelength);
54 }
55
call(ClusterGraphAttributes & GA)56 void FMMMLayout::call(ClusterGraphAttributes &GA)
57 {
58 const Graph &G = GA.constGraph();
59 //compute depth of cluster tree, also sets cluster depth values
60 const ClusterGraph &CG = GA.constClusterGraph();
61 int cdepth = CG.treeDepth();
62 EdgeArray<double> edgeLength(G);
63 //compute lca of end vertices for each edge
64 for(edge e : G.edges)
65 {
66 edgeLength[e] = cdepth - CG.clusterDepth(CG.commonCluster(e->source(),e->target())) + 1;
67 OGDF_ASSERT(edgeLength[e] > 0);
68 }
69 call(GA,edgeLength);
70 GA.updateClusterPositions();
71 }
72
73
call(GraphAttributes & GA,const EdgeArray<double> & edgeLength)74 void FMMMLayout::call(GraphAttributes &GA, const EdgeArray<double> &edgeLength)
75 {
76 const Graph &G = GA.constGraph();
77 NodeArray<NodeAttributes> A(G); //stores the attributes of the nodes (given by L)
78 EdgeArray<EdgeAttributes> E(G); //stores the edge attributes of G
79 Graph G_reduced; //stores a undirected simple and loop-free copy of G
80 EdgeArray<EdgeAttributes> E_reduced; //stores the edge attributes of G_reduced
81 NodeArray<NodeAttributes> A_reduced; //stores the node attributes of G_reduced
82
83 if(G.numberOfNodes() > 1)
84 {
85 GA.clearAllBends();//all edges are straight-line
86 if(useHighLevelOptions())
87 update_low_level_options_due_to_high_level_options_settings();
88 import_NodeAttributes(G,GA,A);
89 import_EdgeAttributes(G,edgeLength,E);
90
91 double t_total;
92 usedTime(t_total);
93 max_integer_position = pow(2.0,maxIntPosExponent());
94 init_ind_ideal_edgelength(G,A,E);
95 make_simple_loopfree(G,A,E,G_reduced,A_reduced,E_reduced);
96 call_DIVIDE_ET_IMPERA_step(G_reduced,A_reduced,E_reduced);
97 adjust_positions(G_reduced, A_reduced);
98 time_total = usedTime(t_total);
99
100 export_NodeAttributes(G_reduced,A_reduced,GA);
101 }
102 else //trivial cases
103 {
104 if(G.numberOfNodes() == 1 )
105 {
106 node v = G.firstNode();
107 GA.x(v) = 0;
108 GA.y(v) = 0;
109 }
110 }
111 }
112
113
call(GraphAttributes & AG,char * ps_file)114 void FMMMLayout::call(GraphAttributes &AG, char* ps_file)
115 {
116 call(AG);
117 create_postscript_drawing(AG,ps_file);
118 }
119
120
call(GraphAttributes & AG,const EdgeArray<double> & edgeLength,char * ps_file)121 void FMMMLayout::call(
122 GraphAttributes &AG,
123 const EdgeArray<double> &edgeLength,
124 char* ps_file)
125 {
126 call(AG,edgeLength);
127 create_postscript_drawing(AG,ps_file);
128 }
129
130
call_DIVIDE_ET_IMPERA_step(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E)131 void FMMMLayout::call_DIVIDE_ET_IMPERA_step(
132 Graph& G,
133 NodeArray<NodeAttributes>& A,
134 EdgeArray<EdgeAttributes>& E)
135 {
136 NodeArray<int> component(G); //holds for each node the index of its component
137 number_of_components = connectedComponents(G,component);//calculate components of G
138 Graph* G_sub = new Graph[number_of_components];
139 NodeArray<NodeAttributes>* A_sub = new NodeArray<NodeAttributes>[number_of_components];
140 EdgeArray<EdgeAttributes>* E_sub = new EdgeArray<EdgeAttributes>[number_of_components];
141 create_maximum_connected_subGraphs(G,A,E,G_sub,A_sub,E_sub,component);
142
143 if(number_of_components == 1)
144 call_MULTILEVEL_step_for_subGraph(G_sub[0],A_sub[0],E_sub[0]);
145 else
146 for(int i = 0; i < number_of_components;i++)
147 call_MULTILEVEL_step_for_subGraph(G_sub[i],A_sub[i],E_sub[i]);
148
149 pack_subGraph_drawings (A,G_sub,A_sub);
150 delete_all_subGraphs(G_sub,A_sub,E_sub);
151 }
152
153
call_MULTILEVEL_step_for_subGraph(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E)154 void FMMMLayout::call_MULTILEVEL_step_for_subGraph(
155 Graph& G,
156 NodeArray<NodeAttributes>& A,
157 EdgeArray<EdgeAttributes>& E)
158 {
159 energybased::fmmm::Multilevel Mult;
160
161 int max_level = 30;//sufficient for all graphs with upto pow(2,30) nodes!
162 //adapt mingraphsize such that no levels are created beyond input graph.
163 if (m_singleLevel) m_minGraphSize = G.numberOfNodes();
164 Array<Graph*> G_mult_ptr(max_level+1);
165 Array<NodeArray<NodeAttributes>*> A_mult_ptr (max_level+1);
166 Array<EdgeArray<EdgeAttributes>*> E_mult_ptr (max_level+1);
167
168 Mult.create_multilevel_representations(G,A,E,randSeed(),
169 galaxyChoice(),minGraphSize(),
170 randomTries(),G_mult_ptr,A_mult_ptr,
171 E_mult_ptr,max_level);
172
173 for(int i = max_level;i >= 0;i--)
174 {
175 if(i == max_level)
176 create_initial_placement(*G_mult_ptr[i],*A_mult_ptr[i]);
177 else
178 {
179 Mult.find_initial_placement_for_level(i,initialPlacementMult(),G_mult_ptr, A_mult_ptr,E_mult_ptr);
180 update_boxlength_and_cornercoordinate(*G_mult_ptr[i],*A_mult_ptr[i]);
181 }
182 call_FORCE_CALCULATION_step(*G_mult_ptr[i],*A_mult_ptr[i],*E_mult_ptr[i], i,max_level);
183 }
184 Mult.delete_multilevel_representations(G_mult_ptr,A_mult_ptr,E_mult_ptr,max_level);
185 }
186
running(int iter,int max_mult_iter,double actforcevectorlength)187 bool FMMMLayout::running(int iter, int max_mult_iter, double actforcevectorlength)
188 {
189 const int ITERBOUND = 10000;
190 switch (stopCriterion()) {
191 case FMMMOptions::StopCriterion::FixedIterations:
192 return iter <= max_mult_iter;
193 case FMMMOptions::StopCriterion::Threshold:
194 return actforcevectorlength >= threshold() && iter <= ITERBOUND;
195 case FMMMOptions::StopCriterion::FixedIterationsOrThreshold:
196 return iter <= max_mult_iter && actforcevectorlength >= threshold();
197 }
198 return false;
199 }
200
call_FORCE_CALCULATION_step(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E,int act_level,int max_level)201 void FMMMLayout::call_FORCE_CALCULATION_step(
202 Graph& G,
203 NodeArray<NodeAttributes>&A,
204 EdgeArray<EdgeAttributes>& E,
205 int act_level,
206 int max_level)
207 {
208 if(G.numberOfNodes() > 1)
209 {
210 int iter = 1;
211 int max_mult_iter = get_max_mult_iter(act_level,max_level,G.numberOfNodes());
212 double actforcevectorlength = threshold() + 1;
213
214 NodeArray<DPoint> F_rep(G); //stores rep. forces
215 NodeArray<DPoint> F_attr(G); //stores attr. forces
216 NodeArray<DPoint> F (G); //stores resulting forces
217 NodeArray<DPoint> last_node_movement(G); //stores the force vectors F of the last iterations (needed to avoid oscillations)
218
219 set_average_ideal_edgelength(G,E);//needed for easy scaling of the forces
220 make_initialisations_for_rep_calc_classes(G);
221
222 while (running(iter, max_mult_iter, actforcevectorlength)) {
223 calculate_forces(G,A,E,F,F_attr,F_rep,last_node_movement,iter,0);
224 if(stopCriterion() != FMMMOptions::StopCriterion::FixedIterations)
225 actforcevectorlength = get_average_forcevector_length(G,F);
226 iter++;
227 }
228
229 if(act_level == 0)
230 call_POSTPROCESSING_step(G,A,E,F,F_attr,F_rep,last_node_movement);
231
232 deallocate_memory_for_rep_calc_classes();
233 }
234 }
235
236
call_POSTPROCESSING_step(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E,NodeArray<DPoint> & F,NodeArray<DPoint> & F_attr,NodeArray<DPoint> & F_rep,NodeArray<DPoint> & last_node_movement)237 void FMMMLayout::call_POSTPROCESSING_step(
238 Graph& G,
239 NodeArray<NodeAttributes>& A,
240 EdgeArray<EdgeAttributes>& E,
241 NodeArray<DPoint>& F,
242 NodeArray<DPoint>& F_attr,
243 NodeArray<DPoint>& F_rep,
244 NodeArray<DPoint>& last_node_movement)
245 {
246 for(int i = 1; i<= 10; i++)
247 calculate_forces(G,A,E,F,F_attr,F_rep,last_node_movement,i,1);
248
249 if(resizeDrawing())
250 {
251 adapt_drawing_to_ideal_average_edgelength(G,A,E);
252 update_boxlength_and_cornercoordinate(G,A);
253 }
254
255 for(int i = 1; i<= fineTuningIterations(); i++)
256 calculate_forces(G,A,E,F,F_attr,F_rep,last_node_movement,i,2);
257
258 if(resizeDrawing())
259 adapt_drawing_to_ideal_average_edgelength(G,A,E);
260 }
261
262
initialize_all_options()263 void FMMMLayout::initialize_all_options()
264 {
265 //setting high level options
266 useHighLevelOptions(false);
267 pageFormat(FMMMOptions::PageFormatType::Square);
268 unitEdgeLength(LayoutStandards::defaultNodeSeparation());
269 newInitialPlacement(false);
270 qualityVersusSpeed(FMMMOptions::QualityVsSpeed::BeautifulAndFast);
271
272 //setting low level options
273 //setting general options
274 randSeed(100);
275 edgeLengthMeasurement(FMMMOptions::EdgeLengthMeasurement::BoundingCircle);
276 allowedPositions(FMMMOptions::AllowedPositions::Integer);
277 maxIntPosExponent(40);
278
279 //setting options for the divide et impera step
280 pageRatio(1.0);
281 stepsForRotatingComponents(10);
282 tipOverCCs(FMMMOptions::TipOver::NoGrowingRow);
283 minDistCC(LayoutStandards::defaultCCSeparation());
284 presortCCs(FMMMOptions::PreSort::DecreasingHeight);
285
286 //setting options for the multilevel step
287 minGraphSize(50);
288 galaxyChoice(FMMMOptions::GalaxyChoice::NonUniformProbLowerMass);
289 randomTries(20);
290 maxIterChange(FMMMOptions::MaxIterChange::LinearlyDecreasing);
291 maxIterFactor(10);
292 initialPlacementMult(FMMMOptions::InitialPlacementMult::Advanced);
293 m_singleLevel = false;
294
295 //setting options for the force calculation step
296 forceModel(FMMMOptions::ForceModel::New);
297 springStrength(1);
298 repForcesStrength(1);
299 repulsiveForcesCalculation(FMMMOptions::RepulsiveForcesMethod::NMM);
300 stopCriterion(FMMMOptions::StopCriterion::FixedIterationsOrThreshold);
301 threshold(0.01);
302 fixedIterations(30);
303 forceScalingFactor(0.05);
304 coolTemperature(false);
305 coolValue(0.99);
306 initialPlacementForces(FMMMOptions::InitialPlacementForces::RandomRandIterNr);
307
308 //setting options for postprocessing
309 resizeDrawing(true);
310 resizingScalar(1);
311 fineTuningIterations(20);
312 fineTuneScalar(0.2);
313 adjustPostRepStrengthDynamically(true);
314 postSpringStrength(2.0);
315 postStrengthOfRepForces(0.01);
316
317 //setting options for different repulsive force calculation methods
318 frGridQuotient(2);
319 nmTreeConstruction(FMMMOptions::ReducedTreeConstruction::SubtreeBySubtree);
320 nmSmallCell(FMMMOptions::SmallestCellFinding::Iteratively);
321 nmParticlesInLeaves(25);
322 nmPrecision(4);
323 }
324
325
update_low_level_options_due_to_high_level_options_settings()326 void FMMMLayout::update_low_level_options_due_to_high_level_options_settings()
327 {
328 FMMMOptions::PageFormatType pf = pageFormat();
329 double uel = unitEdgeLength();
330 bool nip = newInitialPlacement();
331 FMMMOptions::QualityVsSpeed qvs = qualityVersusSpeed();
332
333 //update
334 initialize_all_options();
335 useHighLevelOptions(true);
336 pageFormat(pf);
337 unitEdgeLength(uel);
338 newInitialPlacement(nip);
339 qualityVersusSpeed(qvs);
340
341 switch (pageFormat()) {
342 case FMMMOptions::PageFormatType::Square:
343 pageRatio(1.0);
344 break;
345 case FMMMOptions::PageFormatType::Landscape:
346 pageRatio(1.4142);
347 break;
348 case FMMMOptions::PageFormatType::Portrait:
349 pageRatio(0.7071);
350 }
351
352 if(newInitialPlacement())
353 initialPlacementForces(FMMMOptions::InitialPlacementForces::RandomTime);
354 else
355 initialPlacementForces(FMMMOptions::InitialPlacementForces::RandomRandIterNr);
356
357 switch (qualityVersusSpeed()) {
358 case FMMMOptions::QualityVsSpeed::GorgeousAndEfficient:
359 fixedIterations(60);
360 fineTuningIterations(40);
361 nmPrecision(6);
362 break;
363 case FMMMOptions::QualityVsSpeed::BeautifulAndFast:
364 fixedIterations(30);
365 fineTuningIterations(20);
366 nmPrecision(4);
367 break;
368 case FMMMOptions::QualityVsSpeed::NiceAndIncredibleSpeed:
369 fixedIterations(15);
370 fineTuningIterations(10);
371 nmPrecision(2);
372 }
373 }
374
375
import_NodeAttributes(const Graph & G,GraphAttributes & GA,NodeArray<NodeAttributes> & A)376 void FMMMLayout::import_NodeAttributes(
377 const Graph& G,
378 GraphAttributes& GA,
379 NodeArray<NodeAttributes>& A)
380 {
381 DPoint position;
382
383 for(node v : G.nodes)
384 {
385 position.m_x = GA.x(v);
386 position.m_y = GA.y(v);
387 A[v].set_NodeAttributes(GA.width(v),GA.height(v),position,nullptr,nullptr);
388 }
389 }
390
391
import_EdgeAttributes(const Graph & G,const EdgeArray<double> & edgeLength,EdgeArray<EdgeAttributes> & E)392 void FMMMLayout::import_EdgeAttributes(
393 const Graph& G,
394 const EdgeArray<double>& edgeLength,
395 EdgeArray<EdgeAttributes>& E)
396 {
397 double length;
398
399 for(edge e : G.edges)
400 {
401 if(edgeLength[e] > 0) //no negative edgelength allowed
402 length = edgeLength[e];
403 else
404 length = 1;
405
406 E[e].set_EdgeAttributes(length,nullptr,nullptr);
407 }
408 }
409
410
init_ind_ideal_edgelength(const Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E)411 void FMMMLayout::init_ind_ideal_edgelength(
412 const Graph& G,
413 NodeArray<NodeAttributes>& A,
414 EdgeArray<EdgeAttributes>& E)
415 {
416 switch (edgeLengthMeasurement()) {
417 case FMMMOptions::EdgeLengthMeasurement::Midpoint:
418 for(edge e : G.edges)
419 E[e].set_length(E[e].get_length() * unitEdgeLength());
420 break;
421 case FMMMOptions::EdgeLengthMeasurement::BoundingCircle:
422 set_radii(G, A);
423 for(edge e : G.edges) {
424 E[e].set_length(E[e].get_length() * unitEdgeLength()
425 + radius[e->source()] + radius[e->target()]);
426 }
427 }
428 }
429
430
set_radii(const Graph & G,NodeArray<NodeAttributes> & A)431 void FMMMLayout::set_radii(const Graph& G, NodeArray<NodeAttributes>& A)
432 {
433 radius.init(G);
434 for(node v : G.nodes)
435 {
436 double w = A[v].get_width()/2;
437 double h = A[v].get_height()/2;
438 radius[v] = sqrt(w*w+ h*h);
439 }
440 }
441
442
export_NodeAttributes(Graph & G_reduced,NodeArray<NodeAttributes> & A_reduced,GraphAttributes & GA)443 void FMMMLayout::export_NodeAttributes(
444 Graph& G_reduced,
445 NodeArray<NodeAttributes>& A_reduced,
446 GraphAttributes& GA)
447 {
448 for(node v_copy : G_reduced.nodes)
449 {
450 GA.x(A_reduced[v_copy].get_original_node()) = A_reduced[v_copy].get_position().m_x;
451 GA.y(A_reduced[v_copy].get_original_node()) = A_reduced[v_copy].get_position().m_y;
452 }
453 }
454
455
make_simple_loopfree(const Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> E,Graph & G_reduced,NodeArray<NodeAttributes> & A_reduced,EdgeArray<EdgeAttributes> & E_reduced)456 void FMMMLayout::make_simple_loopfree(
457 const Graph& G,
458 NodeArray<NodeAttributes>& A,
459 EdgeArray<EdgeAttributes>E,
460 Graph& G_reduced,
461 NodeArray<NodeAttributes>& A_reduced,
462 EdgeArray<EdgeAttributes>& E_reduced)
463 {
464 //create the reduced Graph G_reduced and save in A/E links to node/edges of G_reduced
465 //create G_reduced as a copy of G without selfloops!
466
467 G_reduced.clear();
468 for (node v_orig : G.nodes)
469 A[v_orig].set_copy_node(G_reduced.newNode());
470
471 for (edge e_orig : G.edges)
472 {
473 node u_orig = e_orig->source();
474 node v_orig = e_orig->target();
475 if (u_orig != v_orig)
476 E[e_orig].set_copy_edge(G_reduced.newEdge(A[u_orig].get_copy_node(),
477 A[v_orig].get_copy_node()));
478 else
479 E[e_orig].set_copy_edge(nullptr);//mark this edge as deleted
480 }
481
482 //remove parallel (and reversed) edges from G_reduced
483 EdgeArray<double> new_edgelength(G_reduced);
484 List<edge> S;
485 S.clear();
486 delete_parallel_edges(G, E, G_reduced, S, new_edgelength);
487
488 //make A_reduced, E_reduced valid for G_reduced
489 A_reduced.init(G_reduced);
490 E_reduced.init(G_reduced);
491
492 //import information for A_reduced, E_reduced and links to the original nodes/edges
493 //of the copy nodes/edges
494 for (node v_orig : G.nodes)
495 {
496 node v_reduced = A[v_orig].get_copy_node();
497 A_reduced[v_reduced].set_NodeAttributes(A[v_orig].get_width(), A[v_orig].
498 get_height(), A[v_orig].get_position(),
499 v_orig, nullptr);
500 }
501 for (edge e_orig : G.edges)
502 {
503 edge e_reduced = E[e_orig].get_copy_edge();
504 if (e_reduced != nullptr)
505 E_reduced[e_reduced].set_EdgeAttributes(E[e_orig].get_length(), e_orig, nullptr);
506 }
507
508 //update edgelength of copy edges in G_reduced associated with a set of parallel
509 //edges in G
510 update_edgelength(S, new_edgelength, E_reduced);
511 }
512
513
delete_parallel_edges(const Graph & G,EdgeArray<EdgeAttributes> & E,Graph & G_reduced,List<edge> & S,EdgeArray<double> & new_edgelength)514 void FMMMLayout::delete_parallel_edges(
515 const Graph& G,
516 EdgeArray<EdgeAttributes>& E,
517 Graph& G_reduced,
518 List<edge>& S,
519 EdgeArray<double>& new_edgelength)
520 {
521 energybased::fmmm::EdgeMaxBucketFunc MaxSort;
522 energybased::fmmm::EdgeMinBucketFunc MinSort;
523 energybased::fmmm::Edge f_act;
524 List<energybased::fmmm::Edge> sorted_edges;
525 EdgeArray<edge> original_edge (G_reduced); //helping array
526 int save_s_index,save_t_index;
527 int counter = 1;
528 Graph* Graph_ptr = &G_reduced;
529
530 //save the original edges for each edge in G_reduced
531 for(edge e_act : G.edges)
532 if(E[e_act].get_copy_edge() != nullptr) //e_act is no self_loops
533 original_edge[E[e_act].get_copy_edge()] = e_act;
534
535 for(edge e_act : G_reduced.edges)
536 {
537 f_act.set_Edge(e_act,Graph_ptr);
538 sorted_edges.pushBack(f_act);
539 }
540
541 sorted_edges.bucketSort(0,G_reduced.numberOfNodes()-1,MaxSort);
542 sorted_edges.bucketSort(0,G_reduced.numberOfNodes()-1,MinSort);
543
544 edge e_save = nullptr;
545
546 //now parallel edges are consecutive in sorted_edges
547 bool firstEdge = true;
548 for (const auto &ei : sorted_edges) {
549 edge e_act = ei.get_edge();
550 int act_s_index = e_act->source()->index();
551 int act_t_index = e_act->target()->index();
552
553 if (!firstEdge) {
554 if( (act_s_index == save_s_index && act_t_index == save_t_index) ||
555 (act_s_index == save_t_index && act_t_index == save_s_index) )
556 {
557 if(counter == 1) //first parallel edge
558 {
559 S.pushBack(e_save);
560 new_edgelength[e_save] = E[original_edge[e_save]].get_length() +
561 E[original_edge[e_act]].get_length();
562 }
563 else //more then two parallel edges
564 new_edgelength[e_save] +=E[original_edge[e_act]].get_length();
565
566 E[original_edge[e_act]].set_copy_edge(nullptr); //mark copy of edge as deleted
567 G_reduced.delEdge(e_act); //delete copy edge in G_reduced
568 counter++;
569 }
570 else
571 {
572 if (counter > 1)
573 {
574 new_edgelength[e_save]/=counter;
575 counter = 1;
576 }
577 save_s_index = act_s_index;
578 save_t_index = act_t_index;
579 e_save = e_act;
580 }
581 } else {
582 firstEdge = false;
583 save_s_index = act_s_index;
584 save_t_index = act_t_index;
585 e_save = e_act;
586 }
587 }
588
589 //treat special case (last edges were multiple edges)
590 if(counter >1)
591 new_edgelength[e_save]/=counter;
592 }
593
594
update_edgelength(List<edge> & S,EdgeArray<double> & new_edgelength,EdgeArray<EdgeAttributes> & E_reduced)595 void FMMMLayout::update_edgelength(
596 List<edge>& S,
597 EdgeArray<double>& new_edgelength,
598 EdgeArray<EdgeAttributes>& E_reduced)
599 {
600 while (!S.empty())
601 {
602 edge e = S.popFrontRet();
603 E_reduced[e].set_length(new_edgelength[e]);
604 }
605 }
606
607
608 #if 0
609 inline double FMMMLayout::get_post_rep_force_strength(int n)
610 {
611 return min(0.2,400.0/double(n));
612 }
613 #endif
614
615
adjust_positions(const Graph & G,NodeArray<NodeAttributes> & A)616 void FMMMLayout::adjust_positions(const Graph& G, NodeArray<NodeAttributes>& A)
617 {
618 switch (allowedPositions()) {
619 case FMMMOptions::AllowedPositions::All:
620 return;
621 case FMMMOptions::AllowedPositions::Integer:
622 //calculate value of max_integer_position
623 max_integer_position = 100 * average_ideal_edgelength
624 * G.numberOfNodes() * G.numberOfNodes();
625 case FMMMOptions::AllowedPositions::Exponent:
626 break;
627 }
628
629 //restrict positions to lie in [-max_integer_position,max_integer_position]
630 //X [-max_integer_position,max_integer_position]
631 for (node v : G.nodes) {
632 if ((A[v].get_x() > max_integer_position) ||
633 (A[v].get_y() > max_integer_position) ||
634 (A[v].get_x() < max_integer_position * (-1.0)) ||
635 (A[v].get_y() < max_integer_position * (-1.0)))
636 {
637 DPoint cross_point;
638 DPoint nullpoint(0, 0);
639 DPoint old_pos(A[v].get_x(), A[v].get_y());
640 DPoint lt(max_integer_position * (-1.0), max_integer_position);
641 DPoint rt(max_integer_position, max_integer_position);
642 DPoint lb(max_integer_position * (-1.0), max_integer_position * (-1.0));
643 DPoint rb(max_integer_position, max_integer_position * (-1.0));
644 DSegment s(nullpoint, old_pos);
645 DSegment left_bound(lb, lt);
646 DSegment right_bound(rb, rt);
647 DSegment top_bound(lt, rt);
648 DSegment bottom_bound(lb, rb);
649
650 // TODO: What to do when IntersectionType::Overlapping is returned?
651 if (s.intersection(left_bound, cross_point) == IntersectionType::SinglePoint)
652 {
653 A[v].set_x(cross_point.m_x);
654 A[v].set_y(cross_point.m_y);
655 }
656 else if (s.intersection(right_bound, cross_point) == IntersectionType::SinglePoint)
657 {
658 A[v].set_x(cross_point.m_x);
659 A[v].set_y(cross_point.m_y);
660 }
661 else if (s.intersection(top_bound, cross_point) == IntersectionType::SinglePoint)
662 {
663 A[v].set_x(cross_point.m_x);
664 A[v].set_y(cross_point.m_y);
665 }
666 else if (s.intersection(bottom_bound, cross_point) == IntersectionType::SinglePoint)
667 {
668 A[v].set_x(cross_point.m_x);
669 A[v].set_y(cross_point.m_y);
670 }
671 else {
672 // if G has only isolated vertices (this matters for n >= 2), the initial placement of these vertices is ok, so having IntersectionType::Overlapping in this case is ok
673 if (G.numberOfEdges() != 0) {
674 std::cout << "Error in FMMMLayout while restricting vertex positions to a boundary box (vertices already in box)" << std::endl;
675 }
676 }
677 }
678 }
679 //make positions integer
680 for (node v : G.nodes)
681 {
682 double new_x = floor(A[v].get_x());
683 double new_y = floor(A[v].get_y());
684 if (new_x < down_left_corner.m_x)
685 {
686 boxlength += 2;
687 down_left_corner.m_x = down_left_corner.m_x - 2;
688 }
689 if (new_y < down_left_corner.m_y)
690 {
691 boxlength += 2;
692 down_left_corner.m_y = down_left_corner.m_y - 2;
693 }
694 A[v].set_x(new_x);
695 A[v].set_y(new_y);
696 }
697 }
698
699
create_postscript_drawing(GraphAttributes & AG,char * ps_file)700 void FMMMLayout::create_postscript_drawing(GraphAttributes& AG, char* ps_file)
701 {
702 std::ofstream out_fmmm(ps_file, std::ios::out);
703 if (!out_fmmm.good()) {
704 std::cout << ps_file << " could not be opened !" << std::endl;
705 }
706
707 const Graph& G = AG.constGraph();
708 double x_min = AG.x(G.firstNode());
709 double x_max = x_min;
710 double y_min = AG.y(G.firstNode());
711 double y_max = y_min;
712 double max_dist;
713 double scale_factor;
714
715 for (node v : G.nodes)
716 {
717 if (AG.x(v) < x_min)
718 x_min = AG.x(v);
719 else if (AG.x(v) > x_max)
720 x_max = AG.x(v);
721 if (AG.y(v) < y_min)
722 y_min = AG.y(v);
723 else if (AG.y(v) > y_max)
724 y_max = AG.y(v);
725 }
726 max_dist = max(x_max - x_min, y_max - y_min);
727 scale_factor = 500.0 / max_dist;
728
729 out_fmmm << "%!PS-Adobe-2.0 " << std::endl;
730 out_fmmm << "%%Pages: 1 " << std::endl;
731 out_fmmm << "% %BoundingBox: " << x_min << " " << x_max << " " << y_min << " " << y_max << std::endl;
732 out_fmmm << "%%EndComments " << std::endl;
733 out_fmmm << "%%" << std::endl;
734 out_fmmm << "%% Circle" << std::endl;
735 out_fmmm << "/ellipse_dict 4 dict def" << std::endl;
736 out_fmmm << "/ellipse {" << std::endl;
737 out_fmmm << " ellipse_dict" << std::endl;
738 out_fmmm << " begin" << std::endl;
739 out_fmmm << " newpath" << std::endl;
740 out_fmmm << " /yrad exch def /xrad exch def /ypos exch def /xpos exch def" << std::endl;
741 out_fmmm << " matrix currentmatrix" << std::endl;
742 out_fmmm << " xpos ypos translate" << std::endl;
743 out_fmmm << " xrad yrad scale" << std::endl;
744 out_fmmm << " 0 0 1 0 360 arc" << std::endl;
745 out_fmmm << " setmatrix" << std::endl;
746 out_fmmm << " closepath" << std::endl;
747 out_fmmm << " end" << std::endl;
748 out_fmmm << "} def" << std::endl;
749 out_fmmm << "%% Nodes" << std::endl;
750 out_fmmm << "/v { " << std::endl;
751 out_fmmm << " /y exch def" << std::endl;
752 out_fmmm << " /x exch def" << std::endl;
753 out_fmmm << "1.000 1.000 0.894 setrgbcolor" << std::endl;
754 out_fmmm << "x y 10.0 10.0 ellipse fill" << std::endl;
755 out_fmmm << "0.000 0.000 0.000 setrgbcolor" << std::endl;
756 out_fmmm << "x y 10.0 10.0 ellipse stroke" << std::endl;
757 out_fmmm << "} def" << std::endl;
758 out_fmmm << "%% Edges" << std::endl;
759 out_fmmm << "/e { " << std::endl;
760 out_fmmm << " /b exch def" << std::endl;
761 out_fmmm << " /a exch def" << std::endl;
762 out_fmmm << " /y exch def" << std::endl;
763 out_fmmm << " /x exch def" << std::endl;
764 out_fmmm << "x y moveto a b lineto stroke" << std::endl;
765 out_fmmm << "} def" << std::endl;
766 out_fmmm << "%% " << std::endl;
767 out_fmmm << "%% INIT " << std::endl;
768 out_fmmm << "20 200 translate" << std::endl;
769 out_fmmm << scale_factor << " " << scale_factor << " scale " << std::endl;
770 out_fmmm << "1 setlinewidth " << std::endl;
771 out_fmmm << "%%BeginProgram " << std::endl;
772
773 for (edge e : G.edges)
774 out_fmmm << AG.x(e->source()) << " " << AG.y(e->source()) << " "
775 << AG.x(e->target()) << " " << AG.y(e->target()) << " e" << std::endl;
776
777 for (node v : G.nodes)
778 out_fmmm << AG.x(v) << " " << AG.y(v) << " v" << std::endl;
779
780 out_fmmm << "%%EndProgram " << std::endl;
781 out_fmmm << "showpage " << std::endl;
782 out_fmmm << "%%EOF " << std::endl;
783 }
784
785
create_maximum_connected_subGraphs(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E,Graph G_sub[],NodeArray<NodeAttributes> A_sub[],EdgeArray<EdgeAttributes> E_sub[],NodeArray<int> & component)786 void FMMMLayout::create_maximum_connected_subGraphs(
787 Graph& G,
788 NodeArray<NodeAttributes>& A,
789 EdgeArray<EdgeAttributes>&E,
790 Graph G_sub [],
791 NodeArray<NodeAttributes> A_sub [],
792 EdgeArray<EdgeAttributes> E_sub [],
793 NodeArray<int>& component)
794 {
795 //create the subgraphs and save links to subgraph nodes/edges in A
796 for (node v_orig : G.nodes)
797 A[v_orig].set_subgraph_node(G_sub[component[v_orig]].newNode());
798
799 for (edge e_orig : G.edges)
800 {
801 node u_orig = e_orig->source();
802 node v_orig = e_orig->target();
803 E[e_orig].set_subgraph_edge(G_sub[component[u_orig]].newEdge
804 (A[u_orig].get_subgraph_node(), A[v_orig].get_subgraph_node()));
805 }
806
807 //make A_sub,E_sub valid for the subgraphs
808 for (int i = 0; i < number_of_components; i++)
809 {
810 A_sub[i].init(G_sub[i]);
811 E_sub[i].init(G_sub[i]);
812 }
813
814 //import information for A_sub,E_sub and links to the original nodes/edges
815 //of the subGraph nodes/edges
816
817 for (node v_orig : G.nodes)
818 {
819 node v_sub = A[v_orig].get_subgraph_node();
820 A_sub[component[v_orig]][v_sub].set_NodeAttributes(A[v_orig].get_width(),
821 A[v_orig].get_height(), A[v_orig].get_position(),
822 v_orig, nullptr);
823 }
824 for (edge e_orig : G.edges)
825 {
826 edge e_sub = E[e_orig].get_subgraph_edge();
827 node v_orig = e_orig->source();
828 E_sub[component[v_orig]][e_sub].set_EdgeAttributes(E[e_orig].get_length(), e_orig, nullptr);
829 }
830 }
831
832
pack_subGraph_drawings(NodeArray<NodeAttributes> & A,Graph G_sub[],NodeArray<NodeAttributes> A_sub[])833 void FMMMLayout::pack_subGraph_drawings(
834 NodeArray<NodeAttributes>& A,
835 Graph G_sub[],
836 NodeArray<NodeAttributes> A_sub[])
837 {
838 double aspect_ratio_area, bounding_rectangles_area;
839 energybased::fmmm::MAARPacking P;
840 List<Rectangle> R;
841
842 if(stepsForRotatingComponents() == 0) //no rotation
843 calculate_bounding_rectangles_of_components(R,G_sub,A_sub);
844 else
845 rotate_components_and_calculate_bounding_rectangles(R,G_sub,A_sub);
846
847 P.pack_rectangles_using_Best_Fit_strategy(R,pageRatio(), presortCCs(),
848 tipOverCCs(),aspect_ratio_area,
849 bounding_rectangles_area);
850 export_node_positions(A,R,G_sub,A_sub);
851 }
852
853
calculate_bounding_rectangles_of_components(List<Rectangle> & R,Graph G_sub[],NodeArray<NodeAttributes> A_sub[])854 void FMMMLayout::calculate_bounding_rectangles_of_components(
855 List<Rectangle>& R,
856 Graph G_sub[],
857 NodeArray<NodeAttributes> A_sub[])
858 {
859 int i;
860 Rectangle r;
861 R.clear();
862
863 for(i=0;i<number_of_components;i++)
864 {
865 r = calculate_bounding_rectangle(G_sub[i],A_sub[i],i);
866 R.pushBack(r);
867 }
868 }
869
870
calculate_bounding_rectangle(Graph & G,NodeArray<NodeAttributes> & A,int componenet_index)871 energybased::fmmm::Rectangle FMMMLayout::calculate_bounding_rectangle(
872 Graph& G,
873 NodeArray<NodeAttributes>& A,
874 int componenet_index)
875 {
876 Rectangle r;
877 node v = G.firstNode();
878 // max_boundary is the maximum of half of the width and half of the
879 // height of each node; (needed to be able to tip rectangles over
880 // without having access to the height and width of each node)
881 double max_boundary = max(A[v].get_width() / 2, A[v].get_height() / 2);
882 double
883 x_min = A[v].get_x() - max_boundary,
884 x_max = A[v].get_x() + max_boundary,
885 y_min = A[v].get_y() - max_boundary,
886 y_max = A[v].get_y() + max_boundary;
887
888 for (v = v->succ(); v; v = v->succ()) {
889 max_boundary = max(A[v].get_width()/2, A[v].get_height()/2);
890 const double
891 act_x_min = A[v].get_x() - max_boundary,
892 act_x_max = A[v].get_x() + max_boundary,
893 act_y_min = A[v].get_y() - max_boundary,
894 act_y_max = A[v].get_y() + max_boundary;
895 if (act_x_min < x_min) x_min = act_x_min;
896 if (act_x_max > x_max) x_max = act_x_max;
897 if (act_y_min < y_min) y_min = act_y_min;
898 if (act_y_max > y_max) y_max = act_y_max;
899 }
900
901 //add offset
902 x_min -= minDistCC()/2;
903 x_max += minDistCC()/2;
904 y_min -= minDistCC()/2;
905 y_max += minDistCC()/2;
906
907 r.set_rectangle(x_max-x_min,y_max-y_min,x_min,y_min,componenet_index);
908 return r;
909 }
910
911
rotate_components_and_calculate_bounding_rectangles(List<Rectangle> & R,Graph G_sub[],NodeArray<NodeAttributes> A_sub[])912 void FMMMLayout::rotate_components_and_calculate_bounding_rectangles(
913 List<Rectangle>&R,
914 Graph G_sub [],
915 NodeArray<NodeAttributes> A_sub [])
916 {
917 Array<NodeArray<DPoint> > best_coords(number_of_components);
918 Array<NodeArray<DPoint> > old_coords(number_of_components);
919 #if 0
920 node v_sub;
921 #endif
922 Rectangle r_act, r_best;
923 DPoint new_pos, new_dlc;
924
925 R.clear(); //make R empty
926
927 for (int i = 0; i < number_of_components; i++)
928 {//allcomponents
929
930 //init r_best, best_area and best_(old)coords
931 r_best = calculate_bounding_rectangle(G_sub[i], A_sub[i], i);
932 double best_area = calculate_area(r_best.get_width(), r_best.get_height(),
933 number_of_components);
934 best_coords[i].init(G_sub[i]);
935 old_coords[i].init(G_sub[i]);
936
937 for (node v_sub : G_sub[i].nodes)
938 old_coords[i][v_sub] = best_coords[i][v_sub] = A_sub[i][v_sub].get_position();
939
940 //rotate the components
941 for (int j = 1; j <= stepsForRotatingComponents(); j++)
942 {
943 //calculate new positions for the nodes, the new rectangle and area
944 double angle = Math::pi_2 * (double(j) / double(stepsForRotatingComponents() + 1));
945 double sin_j = sin(angle);
946 double cos_j = cos(angle);
947 for (node v_sub : G_sub[i].nodes)
948 {
949 new_pos.m_x = cos_j * old_coords[i][v_sub].m_x
950 - sin_j * old_coords[i][v_sub].m_y;
951 new_pos.m_y = sin_j * old_coords[i][v_sub].m_x
952 + cos_j * old_coords[i][v_sub].m_y;
953 A_sub[i][v_sub].set_position(new_pos);
954 }
955
956 r_act = calculate_bounding_rectangle(G_sub[i], A_sub[i], i);
957 double act_area = calculate_area(r_act.get_width(), r_act.get_height(),
958 number_of_components);
959
960 double act_area_PI_half_rotated;
961 if (number_of_components == 1)
962 act_area_PI_half_rotated = calculate_area(r_act.get_height(),
963 r_act.get_width(),
964 number_of_components);
965
966 //store placement of the nodes with minimal area (in case that
967 //number_of_components >1) else store placement with minimal aspect ratio area
968 if (act_area < best_area)
969 {
970 r_best = r_act;
971 best_area = act_area;
972 for (node v_sub : G_sub[i].nodes)
973 best_coords[i][v_sub] = A_sub[i][v_sub].get_position();
974 }
975 else if ((number_of_components == 1) && (act_area_PI_half_rotated < best_area))
976 { //test if rotating further with PI_half would be an improvement
977 r_best = r_act;
978 best_area = act_area_PI_half_rotated;
979 for (node v_sub : G_sub[i].nodes)
980 best_coords[i][v_sub] = A_sub[i][v_sub].get_position();
981 //the needed rotation step follows in the next if statement
982 }
983 }
984
985 //tipp the smallest rectangle over by angle PI/2 around the origin if it makes the
986 //aspect_ratio of r_best more similar to the desired aspect_ratio
987 double ratio = r_best.get_width() / r_best.get_height();
988
989 if ((pageRatio() < 1 && ratio > 1) || (pageRatio() >= 1 && ratio < 1))
990 {
991 for (node v_sub : G_sub[i].nodes)
992 {
993 new_pos.m_x = best_coords[i][v_sub].m_y*(-1);
994 new_pos.m_y = best_coords[i][v_sub].m_x;
995 best_coords[i][v_sub] = new_pos;
996 }
997
998 //calculate new rectangle
999 new_dlc.m_x = r_best.get_old_dlc_position().m_y*(-1) - r_best.get_height();
1000 new_dlc.m_y = r_best.get_old_dlc_position().m_x;
1001
1002 double new_width = r_best.get_height();
1003 double new_height = r_best.get_width();
1004 r_best.set_width(new_width);
1005 r_best.set_height(new_height);
1006 r_best.set_old_dlc_position(new_dlc);
1007 }
1008
1009 //save the computed information in A_sub and R
1010 for (node v_sub : G_sub[i].nodes)
1011 A_sub[i][v_sub].set_position(best_coords[i][v_sub]);
1012 R.pushBack(r_best);
1013 }
1014 }
1015
export_node_positions(NodeArray<NodeAttributes> & A,List<Rectangle> & R,Graph G_sub[],NodeArray<NodeAttributes> A_sub[])1016 void FMMMLayout::export_node_positions(
1017 NodeArray<NodeAttributes>& A,
1018 List<Rectangle>& R,
1019 Graph G_sub [],
1020 NodeArray<NodeAttributes> A_sub [])
1021 {
1022 for (const Rectangle &r : R)
1023 {
1024 int i = r.get_component_index();
1025 if (r.is_tipped_over())
1026 {
1027 //calculate tipped coordinates of the nodes
1028 for (node v_sub : G_sub[i].nodes)
1029 {
1030 DPoint tipped_pos(-A_sub[i][v_sub].get_y(), A_sub[i][v_sub].get_x());
1031 A_sub[i][v_sub].set_position(tipped_pos);
1032 }
1033 }
1034
1035 for (node v_sub : G_sub[i].nodes)
1036 {
1037 DPoint newpos = A_sub[i][v_sub].get_position() + r.get_new_dlc_position()
1038 - r.get_old_dlc_position();
1039 A[A_sub[i][v_sub].get_original_node()].set_position(newpos);
1040 }
1041 }
1042 }
1043
1044
get_max_mult_iter(int act_level,int max_level,int node_nr)1045 inline int FMMMLayout::get_max_mult_iter(int act_level, int max_level, int node_nr)
1046 {
1047 int iter;
1048 switch (maxIterChange()) {
1049 case FMMMOptions::MaxIterChange::Constant:
1050 iter = fixedIterations();
1051 break;
1052 case FMMMOptions::MaxIterChange::LinearlyDecreasing:
1053 if (max_level == 0) {
1054 iter = maxIterFactor() * fixedIterations();
1055 } else {
1056 iter = fixedIterations() +
1057 int((double(act_level)/double(max_level)) *
1058 (maxIterFactor() - 1) * fixedIterations());
1059 }
1060 break;
1061 case FMMMOptions::MaxIterChange::RapidlyDecreasing:
1062 switch (max_level - act_level) {
1063 case 0:
1064 iter = fixedIterations() + int((maxIterFactor()-1) * fixedIterations());
1065 break;
1066 case 1:
1067 iter = fixedIterations() + int(0.5 * (maxIterFactor()-1) * fixedIterations());
1068 break;
1069 case 2:
1070 iter = fixedIterations() + int(0.25 * (maxIterFactor()-1) * fixedIterations());
1071 break;
1072 default: // >= 3
1073 iter = fixedIterations();
1074 }
1075 }
1076
1077 //helps to get good drawings for small graphs and graphs with few multilevels
1078 if((node_nr <= 500) && (iter < 100))
1079 return 100;
1080 else
1081 return iter;
1082 }
1083
1084
calculate_forces(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E,NodeArray<DPoint> & F,NodeArray<DPoint> & F_attr,NodeArray<DPoint> & F_rep,NodeArray<DPoint> & last_node_movement,int iter,int fine_tuning_step)1085 inline void FMMMLayout::calculate_forces(
1086 Graph& G,
1087 NodeArray<NodeAttributes>& A,
1088 EdgeArray<EdgeAttributes>& E,
1089 NodeArray<DPoint>& F,
1090 NodeArray<DPoint>& F_attr,
1091 NodeArray<DPoint>& F_rep,
1092 NodeArray<DPoint>& last_node_movement,
1093 int iter,
1094 int fine_tuning_step)
1095 {
1096 adjust_positions(G, A);
1097 calculate_attractive_forces(G,A,E,F_attr);
1098 calculate_repulsive_forces(G,A,F_rep);
1099 add_attr_rep_forces(G,F_attr,F_rep,F,iter,fine_tuning_step);
1100 prevent_oscillations(G,F,last_node_movement,iter);
1101 move_nodes(G,A,F);
1102 update_boxlength_and_cornercoordinate(G,A);
1103 }
1104
1105
init_boxlength_and_cornercoordinate(Graph & G,NodeArray<NodeAttributes> & A)1106 void FMMMLayout::init_boxlength_and_cornercoordinate(
1107 Graph& G,
1108 NodeArray<NodeAttributes>& A)
1109 {
1110 //boxlength is set
1111
1112 const double MIN_NODE_SIZE = 10;
1113 const double BOX_SCALING_FACTOR = 1.1;
1114
1115 double w = 0, h = 0;
1116 for (node v : G.nodes)
1117 {
1118 w += max(A[v].get_width(), MIN_NODE_SIZE);
1119 h += max(A[v].get_height(), MIN_NODE_SIZE);
1120 }
1121
1122 boxlength = ceil(max(w, h) * BOX_SCALING_FACTOR);
1123
1124 //down left corner of comp. box is the origin
1125 down_left_corner.m_x = 0;
1126 down_left_corner.m_y = 0;
1127 }
1128
create_initial_placement_uniform_grid(const Graph & G,NodeArray<NodeAttributes> & A)1129 void FMMMLayout::create_initial_placement_uniform_grid(const Graph& G, NodeArray<NodeAttributes>& A)
1130 {
1131 // set nodes to the midpoints of a grid
1132 int level = static_cast<int>(ceil(Math::log4(G.numberOfNodes())));
1133 OGDF_ASSERT(level < 31);
1134 int m = (1 << level) - 1;
1135 double blall = boxlength / (m + 1); //boxlength for boxes at the lowest level (depth)
1136 Array<node> all_nodes;
1137 G.allNodes(all_nodes);
1138
1139 int k = 0;
1140 node v = all_nodes[0];
1141 for (int i = 0; i <= m; ++i) {
1142 for (int j = 0; j <= m; ++j) {
1143 A[v].set_x(boxlength*i / (m + 1) + blall / 2);
1144 A[v].set_y(boxlength*j / (m + 1) + blall / 2);
1145 if (k == G.numberOfNodes() - 1) {
1146 return;
1147 } else {
1148 k++;
1149 v = all_nodes[k];
1150 }
1151 }
1152 }
1153 }
1154
create_initial_placement_random(const Graph & G,NodeArray<NodeAttributes> & A)1155 void FMMMLayout::create_initial_placement_random(const Graph& G, NodeArray<NodeAttributes>& A)
1156 {
1157 const int BILLION = 1000000000;
1158
1159 for (node v : G.nodes) {
1160 DPoint rndp;
1161 rndp.m_x = double(randomNumber(0, BILLION)) / BILLION; //rand_x in [0,1]
1162 rndp.m_y = double(randomNumber(0, BILLION)) / BILLION; //rand_y in [0,1]
1163 A[v].set_x(rndp.m_x*(boxlength - 2) + 1);
1164 A[v].set_y(rndp.m_y*(boxlength - 2) + 1);
1165 }
1166 }
1167
create_initial_placement(Graph & G,NodeArray<NodeAttributes> & A)1168 void FMMMLayout::create_initial_placement(Graph& G, NodeArray<NodeAttributes>& A)
1169 {
1170 init_boxlength_and_cornercoordinate(G, A);
1171
1172 switch (initialPlacementForces()) {
1173 case FMMMOptions::InitialPlacementForces::KeepPositions:
1174 break;
1175 case FMMMOptions::InitialPlacementForces::UniformGrid:
1176 create_initial_placement_uniform_grid(G, A);
1177 break;
1178 case FMMMOptions::InitialPlacementForces::RandomTime:
1179 setSeed((unsigned int) time(nullptr));
1180 create_initial_placement_random(G, A);
1181 break;
1182 case FMMMOptions::InitialPlacementForces::RandomRandIterNr:
1183 setSeed(randSeed());
1184 create_initial_placement_random(G, A);
1185 }
1186
1187 update_boxlength_and_cornercoordinate(G, A);
1188 }
1189
1190
init_F(Graph & G,NodeArray<DPoint> & F)1191 void FMMMLayout::init_F(Graph& G, NodeArray<DPoint>& F)
1192 {
1193 DPoint nullpoint(0, 0);
1194 for (node v : G.nodes)
1195 F[v] = nullpoint;
1196 }
1197
1198
make_initialisations_for_rep_calc_classes(Graph & G)1199 void FMMMLayout::make_initialisations_for_rep_calc_classes(Graph& G)
1200 {
1201 switch (repulsiveForcesCalculation()) {
1202 case FMMMOptions::RepulsiveForcesMethod::Exact:
1203 FR.make_initialisations(boxlength, down_left_corner, frGridQuotient());
1204 break;
1205 case FMMMOptions::RepulsiveForcesMethod::GridApproximation:
1206 FR.make_initialisations(boxlength, down_left_corner, frGridQuotient());
1207 break;
1208 case FMMMOptions::RepulsiveForcesMethod::NMM:
1209 NM.make_initialisations(G, boxlength, down_left_corner,
1210 nmParticlesInLeaves(), nmPrecision(),
1211 nmTreeConstruction(), nmSmallCell());
1212 }
1213 }
1214
1215
calculate_attractive_forces(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E,NodeArray<DPoint> & F_attr)1216 void FMMMLayout::calculate_attractive_forces(
1217 Graph& G,
1218 NodeArray<NodeAttributes> & A,
1219 EdgeArray<EdgeAttributes> & E,
1220 NodeArray<DPoint>& F_attr)
1221 {
1222 DPoint f_u;
1223 DPoint nullpoint (0,0);
1224
1225 //initialisation
1226 init_F(G,F_attr);
1227
1228 //calculation
1229 for(edge e : G.edges)
1230 {
1231 node u = e->source();
1232 node v = e->target();
1233 DPoint vector_v_minus_u = A[v].get_position() - A[u].get_position();
1234 double norm_v_minus_u = vector_v_minus_u.norm();
1235 if(vector_v_minus_u == nullpoint)
1236 f_u = nullpoint;
1237 else if(!numexcept::f_near_machine_precision(norm_v_minus_u,f_u))
1238 {
1239 double scalar = f_attr_scalar(norm_v_minus_u,E[e].get_length())/norm_v_minus_u;
1240 f_u.m_x = scalar * vector_v_minus_u.m_x;
1241 f_u.m_y = scalar * vector_v_minus_u.m_y;
1242 }
1243
1244 F_attr[v] = F_attr[v] - f_u;
1245 F_attr[u] = F_attr[u] + f_u;
1246 }
1247 }
1248
1249
f_attr_scalar(double d,double ind_ideal_edge_length)1250 double FMMMLayout::f_attr_scalar(double d, double ind_ideal_edge_length)
1251 {
1252 double s(0);
1253
1254 switch (forceModel()) {
1255 case FMMMOptions::ForceModel::FruchtermanReingold:
1256 s = d*d/(ind_ideal_edge_length*ind_ideal_edge_length*ind_ideal_edge_length);
1257 break;
1258 case FMMMOptions::ForceModel::Eades:
1259 {
1260 const double c = 10;
1261 if (d == 0)
1262 s = -1e10;
1263 else
1264 s = c * std::log2(d/ind_ideal_edge_length) / ind_ideal_edge_length;
1265 break;
1266 }
1267 case FMMMOptions::ForceModel::New:
1268 {
1269 const double c = std::log2(d/ind_ideal_edge_length);
1270 if (d > 0)
1271 s = c * d * d /
1272 (ind_ideal_edge_length * ind_ideal_edge_length * ind_ideal_edge_length);
1273 else
1274 s = -1e10;
1275 break;
1276 }
1277 default:
1278 std::cerr << "Error FMMMLayout::f_attr_scalar" << std::endl;
1279 OGDF_ASSERT(false);
1280 }
1281
1282 return s;
1283 }
1284
1285
add_attr_rep_forces(Graph & G,NodeArray<DPoint> & F_attr,NodeArray<DPoint> & F_rep,NodeArray<DPoint> & F,int iter,int fine_tuning_step)1286 void FMMMLayout::add_attr_rep_forces(
1287 Graph& G,
1288 NodeArray<DPoint>& F_attr,
1289 NodeArray<DPoint>& F_rep,
1290 NodeArray<DPoint>& F,
1291 int iter,
1292 int fine_tuning_step)
1293 {
1294 DPoint nullpoint(0, 0);
1295
1296 //set cool_factor
1297 if (!coolTemperature())
1298 cool_factor = 1.0;
1299 else if (coolTemperature() && fine_tuning_step == 0)
1300 {
1301 if (iter == 1)
1302 cool_factor = coolValue();
1303 else
1304 cool_factor *= coolValue();
1305 }
1306
1307 if (fine_tuning_step == 1)
1308 cool_factor /= 10.0; //decrease the temperature rapidly
1309 else if (fine_tuning_step == 2)
1310 {
1311 if (iter <= fineTuningIterations() - 5)
1312 cool_factor = fineTuneScalar(); //decrease the temperature rapidly
1313 else
1314 cool_factor = (fineTuneScalar() / 10.0);
1315 }
1316
1317 //set the values for the spring strength and strength of the rep. force field
1318 double act_spring_strength, act_rep_force_strength;
1319 if (fine_tuning_step <= 1)//usual case
1320 {
1321 act_spring_strength = springStrength();
1322 act_rep_force_strength = repForcesStrength();
1323 }
1324 else if (!adjustPostRepStrengthDynamically())
1325 {
1326 act_spring_strength = postSpringStrength();
1327 act_rep_force_strength = postStrengthOfRepForces();
1328 }
1329 else //adjustPostRepStrengthDynamically())
1330 {
1331 act_spring_strength = postSpringStrength();
1332 act_rep_force_strength = get_post_rep_force_strength(G.numberOfNodes());
1333 }
1334
1335 for (node v : G.nodes)
1336 {
1337 DPoint f;
1338 f.m_x = act_spring_strength * F_attr[v].m_x + act_rep_force_strength * F_rep[v].m_x;
1339 f.m_y = act_spring_strength * F_attr[v].m_y + act_rep_force_strength * F_rep[v].m_y;
1340 f.m_x = average_ideal_edgelength * average_ideal_edgelength * f.m_x;
1341 f.m_y = average_ideal_edgelength * average_ideal_edgelength * f.m_y;
1342
1343 double norm_f = f.norm();
1344
1345 DPoint force;
1346 if (f == nullpoint)
1347 force = nullpoint;
1348 else if (numexcept::f_near_machine_precision(norm_f, force))
1349 restrict_force_to_comp_box(force);
1350 else
1351 {
1352 double scalar = min(norm_f * cool_factor * forceScalingFactor(), max_radius(iter)) / norm_f;
1353 force.m_x = scalar * f.m_x;
1354 force.m_y = scalar * f.m_y;
1355 }
1356 F[v] = force;
1357 }
1358 }
1359
1360
move_nodes(Graph & G,NodeArray<NodeAttributes> & A,NodeArray<DPoint> & F)1361 void FMMMLayout::move_nodes(
1362 Graph& G,
1363 NodeArray<NodeAttributes>& A,
1364 NodeArray<DPoint>& F)
1365 {
1366 for(node v : G.nodes)
1367 A[v].set_position(A[v].get_position() + F[v]);
1368 }
1369
1370
update_boxlength_and_cornercoordinate(Graph & G,NodeArray<NodeAttributes> & A)1371 void FMMMLayout::update_boxlength_and_cornercoordinate(
1372 Graph& G,
1373 NodeArray<NodeAttributes>&A)
1374 {
1375 node vFirst = G.firstNode();
1376 DPoint midpoint = A[vFirst].get_position();
1377
1378 double xmin, xmax, ymin, ymax;
1379 xmin = xmax = midpoint.m_x;
1380 ymin = ymax = midpoint.m_y;
1381
1382 for (node v : G.nodes)
1383 {
1384 midpoint = A[v].get_position();
1385 if (midpoint.m_x < xmin)
1386 xmin = midpoint.m_x;
1387 if (midpoint.m_x > xmax)
1388 xmax = midpoint.m_x;
1389 if (midpoint.m_y < ymin)
1390 ymin = midpoint.m_y;
1391 if (midpoint.m_y > ymax)
1392 ymax = midpoint.m_y;
1393 }
1394
1395 //set down_left_corner and boxlength
1396
1397 down_left_corner.m_x = floor(xmin - 1);
1398 down_left_corner.m_y = floor(ymin - 1);
1399 boxlength = ceil(max(ymax - ymin, xmax - xmin) *1.01 + 2);
1400
1401 //exception handling: all nodes have same x and y coordinate
1402 if (boxlength <= 2)
1403 {
1404 boxlength = G.numberOfNodes() * 20;
1405 down_left_corner.m_x = floor(xmin) - (boxlength / 2);
1406 down_left_corner.m_y = floor(ymin) - (boxlength / 2);
1407 }
1408
1409 //export the boxlength and down_left_corner values to the rep. calc. classes
1410
1411 switch (repulsiveForcesCalculation()) {
1412 case FMMMOptions::RepulsiveForcesMethod::Exact:
1413 case FMMMOptions::RepulsiveForcesMethod::GridApproximation:
1414 FR.update_boxlength_and_cornercoordinate(boxlength, down_left_corner);
1415 break;
1416 case FMMMOptions::RepulsiveForcesMethod::NMM:
1417 NM.update_boxlength_and_cornercoordinate(boxlength, down_left_corner);
1418 }
1419 }
1420
1421
set_average_ideal_edgelength(Graph & G,EdgeArray<EdgeAttributes> & E)1422 void FMMMLayout::set_average_ideal_edgelength(
1423 Graph& G,
1424 EdgeArray<EdgeAttributes>& E)
1425 {
1426 if(G.numberOfEdges() > 0)
1427 {
1428 double averagelength = 0;
1429 for(edge e : G.edges)
1430 averagelength += E[e].get_length();
1431 average_ideal_edgelength = averagelength/G.numberOfEdges();
1432 }
1433 else
1434 average_ideal_edgelength = 50;
1435 }
1436
1437
get_average_forcevector_length(Graph & G,NodeArray<DPoint> & F)1438 double FMMMLayout::get_average_forcevector_length(Graph& G, NodeArray<DPoint>& F)
1439 {
1440 double lengthsum = 0;
1441 for (node v : G.nodes)
1442 lengthsum += F[v].norm();
1443 lengthsum /= G.numberOfNodes();
1444 return lengthsum;
1445 }
1446
1447
prevent_oscillations(Graph & G,NodeArray<DPoint> & F,NodeArray<DPoint> & last_node_movement,int iter)1448 void FMMMLayout::prevent_oscillations(
1449 Graph& G,
1450 NodeArray<DPoint>& F,
1451 NodeArray<DPoint>& last_node_movement,
1452 int iter)
1453 {
1454 const double pi_times_1_over_6 = 0.52359878;
1455 const double factors[] = {
1456 2.0, 2.0, 1.5, 1.0, 0.66666666, 0.5, 0.33333333,
1457 0.33333333, 0.5, 0.66666666, 1.0, 1.5, 2.0, 2.0
1458 };
1459 const DPoint nullpoint(0, 0);
1460
1461 if (iter > 1) { // usual case
1462 for (node v : G.nodes) {
1463 const DPoint force_new(F[v]);
1464 const DPoint force_old(last_node_movement[v]);
1465 const double norm_new = F[v].norm();
1466 const double norm_old = last_node_movement[v].norm();
1467 if (norm_new > 0 && norm_old > 0) {
1468 const double fi = nullpoint.angle(force_old, force_new);
1469 const double factor = factors[int(std::ceil(fi / pi_times_1_over_6))];
1470 const double quot = norm_old * factor / norm_new;
1471 if (quot < 1.0) {
1472 F[v].m_x = quot * F[v].m_x;
1473 F[v].m_y = quot * F[v].m_y;
1474 }
1475 }
1476 last_node_movement[v] = F[v];
1477 }
1478 }
1479 else if (iter == 1)
1480 init_last_node_movement(G,F,last_node_movement);
1481 }
1482
1483
init_last_node_movement(Graph & G,NodeArray<DPoint> & F,NodeArray<DPoint> & last_node_movement)1484 void FMMMLayout::init_last_node_movement(
1485 Graph& G,
1486 NodeArray<DPoint>& F,
1487 NodeArray<DPoint>& last_node_movement)
1488 {
1489 for(node v : G.nodes)
1490 last_node_movement[v]= F[v];
1491 }
1492
1493
adapt_drawing_to_ideal_average_edgelength(Graph & G,NodeArray<NodeAttributes> & A,EdgeArray<EdgeAttributes> & E)1494 void FMMMLayout::adapt_drawing_to_ideal_average_edgelength(
1495 Graph& G,
1496 NodeArray<NodeAttributes>& A,
1497 EdgeArray<EdgeAttributes>& E)
1498 {
1499 double sum_real_edgelength = 0;
1500 double sum_ideal_edgelength = 0;
1501 for (edge e : G.edges)
1502 {
1503 sum_ideal_edgelength += E[e].get_length();
1504 sum_real_edgelength += (A[e->source()].get_position() - A[e->target()].get_position()).norm();
1505 }
1506
1507 double area_scaling_factor;
1508 if (sum_real_edgelength == 0) // very very unlikly case
1509 area_scaling_factor = 1;
1510 else
1511 area_scaling_factor = sum_ideal_edgelength / sum_real_edgelength;
1512
1513 DPoint new_pos;
1514 for (node v : G.nodes)
1515 {
1516 new_pos.m_x = resizingScalar() * area_scaling_factor * A[v].get_position().m_x;
1517 new_pos.m_y = resizingScalar() * area_scaling_factor * A[v].get_position().m_y;
1518 A[v].set_position(new_pos);
1519 }
1520 }
1521
1522 }
1523