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