6 * $Date: 2012-07-07 17:14:54 +0200 (Sa, 07. Jul 2012) $
7 ***************************************************************/
10 * \brief Implementation of Kamada-Kaway layout algorithm.
12 * \author Karsten Klein
15 * This file is part of the Open Graph Drawing Framework (OGDF).
19 * See README.txt in the root directory of the OGDF installation for details.
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License
24 * Version 2 or 3 as published by the Free Software Foundation;
25 * see the file LICENSE.txt included in the packaging of this file
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
35 * You should have received a copy of the GNU General Public
36 * License along with this program; if not, write to the Free
37 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
38 * Boston, MA 02110-1301, USA.
40 * \see http://www.gnu.org/copyleft/gpl.html
41 ***************************************************************/
43 #include <ogdf/energybased/SpringEmbedderKK.h>
44 #include <ogdf/basic/SList.h>
47 #include <ogdf/basic/simple_graph_alg.h>
50 const double SpringEmbedderKK::startVal
= DBL_MAX
- 1.0;
51 const double SpringEmbedderKK::minVal
= DBL_MIN
;
52 const double SpringEmbedderKK::desMinLength
= 0.0001;
54 void SpringEmbedderKK::initialize(
56 NodeArray
<dpair
>& partialDer
,
57 const EdgeArray
<double>& eLength
,
58 NodeArray
< NodeArray
<double> >& oLength
,
59 NodeArray
< NodeArray
<double> >& sstrength
,
64 const Graph
&G
= GA
.constGraph();
65 //hier if m_spread vorlayout oder muss der Nutzer das extern erledigen?
66 m_prevEnergy
= startVal
;
67 m_prevLEnergy
= startVal
;
69 // all edges straight-line
74 //the shortest path lengths
75 forall_nodes (v
, G
) oLength
[v
].init(G
, DBL_MAX
);
77 //-------------------------------------
78 //computes shortest path distances d_ij
79 //-------------------------------------
82 //we use simply BFS n times
83 //TODO experimentally compare speed, also with bintree dijkstra
86 // usedTime(timeUsed);
88 maxDist
= allpairsspBFS(G
, oLength
);
90 // timeUsed = usedTime(timeUsed);
91 // cout << "\n******APSP BFS runtime: \n";
96 EdgeArray
<double> adaptedLength(G
);
97 adaptLengths(G
, GA
, eLength
, adaptedLength
);
98 //we use simply the BFM n times or Floyd instead, leading to cubic runtime
99 //TODO experimentally compare speed, also with bintree dijkstra
100 maxDist
= allpairssp(G
, adaptedLength
, oLength
, DBL_MAX
);
102 //------------------------------------
103 //computes original spring length l_ij
104 //------------------------------------
106 //first we determine desirable edge length L
107 //nodes sizes may be non-uniform, we approximate the display size (grid)
108 //this part relies on the fact that node sizes are set != zero
109 //TODO check later if this is a good choice
110 double L
= m_desLength
; //desirable length
111 double Lzero
; //Todo check with m_zeroLength
112 if (L
< desMinLength
)
114 double swidth
= 0.0f
, sheight
= 0.0f
;
116 // Do all nodes lie on the same point? Check by computing BB of centers
117 // Then, perform simple shifting in layout
118 node vFirst
= G
.firstNode();
119 double minX
= GA
.x(vFirst
), maxX
= GA
.x(vFirst
),
120 minY
= GA
.y(vFirst
), maxY
= GA
.y(vFirst
);
122 // add node sizes to estimate desirable length
123 // compute BB to check degeneracy
126 swidth
+= GA
.width(v
);
127 sheight
+= GA
.height(v
);
129 if(GA
.x(v
) < minX
) minX
= GA
.x(v
);
130 if(GA
.x(v
) > maxX
) maxX
= GA
.x(v
);
131 if(GA
.y(v
) < minY
) minY
= GA
.y(v
);
132 if(GA
.y(v
) > maxY
) maxY
= GA
.y(v
);
135 double sroot
= maxDist
;//sqrt(G.numberOfNodes());
136 swidth
= swidth
/ sroot
;
137 sheight
= sheight
/ sroot
;
138 Lzero
= max(2.0*sroot
, 2.0*(swidth
+ sheight
));
139 //test for multilevel
140 Lzero
= max(max(maxX
-minX
, maxY
-minY
), 2.0*Lzero
);
141 //cout << "Lzero: "<<Lzero<<"\n";
146 // cout << "Desirable edge length computed: "<<L<<"\n";
149 //--------------------------------------------------
150 // Having L we can compute the original lengths l_ij
151 // Computes spring strengths k_ij
152 //--------------------------------------------------
157 sstrength
[v
].init(G
);
163 sstrength
[v
][w
] = minVal
;
167 oLength
[v
][w
] = L
* dij
;
168 sstrength
[v
][w
] = m_K
/ (dij
* dij
);
175 void SpringEmbedderKK::mainStep(GraphAttributes
& GA
,
176 NodeArray
<dpair
>& partialDer
,
177 NodeArray
< NodeArray
<double> >& oLength
,
178 NodeArray
< NodeArray
<double> >& sstrength
,
179 const double maxDist
)
181 const Graph
&G
= GA
.constGraph();
185 NodeArray
<int> nodeCounts(G
, 0);
186 int nodeCount
= 0; //number of moved nodes
188 // Now we compute delta_m, we search for the node with max value
189 double delta_m
= 0.0f
;
191 node best_m
= G
.firstNode();
193 // Compute the partial derivatives first
196 dpair parder
= computeParDers(v
, GA
, sstrength
, oLength
);
197 partialDer
[v
] = parder
;
198 //delta_m is sqrt of squares of partial derivatives
199 delta_v
= sqrt(parder
.x1()*parder
.x1() + parder
.x2()*parder
.x2());
201 if (delta_v
> delta_m
)
208 int globalItCount
, localItCount
;
211 globalItCount
= m_gItBaseVal
+m_gItFactor
*G
.numberOfNodes();
212 localItCount
= 2*G
.numberOfNodes();
216 globalItCount
= m_maxGlobalIt
;
217 localItCount
= m_maxLocalIt
;
221 while (globalItCount
-- > 0 && !finished(delta_m
))
224 // cout <<"G: "<<globalItCount <<"\n";
225 // cout <<"New iteration on "<<best_m->index()<<"\n";
227 nodeCounts
[best_m
]++;
229 // The contribution best_m makes to the partial derivatives of
231 NodeArray
<dpair
> p_partials(G
);
234 p_partials
[v
] = computeParDer(v
, best_m
, GA
, sstrength
, oLength
);
240 // cout <<" New local iteration\n";
241 // cout <<" L: "<<localItCount <<"\n";
243 // Compute the 4 elements of the Jacobian
244 double dE_dx_dx
= 0.0f
, dE_dx_dy
= 0.0f
, dE_dy_dx
= 0.0f
, dE_dy_dy
= 0.0f
;
248 double x_diff
= GA
.x(best_m
) - GA
.x(v
);
249 double y_diff
= GA
.y(best_m
) - GA
.y(v
);
250 double dist
= sqrt(x_diff
* x_diff
+ y_diff
* y_diff
);
251 double dist3
= dist
* dist
* dist
;
252 OGDF_ASSERT(dist3
!= 0.0);
253 double k_mi
= sstrength
[best_m
][v
];
254 double l_mi
= oLength
[best_m
][v
];
255 dE_dx_dx
+= k_mi
* (1 - (l_mi
* y_diff
* y_diff
)/dist3
);
256 dE_dx_dy
+= k_mi
* l_mi
* x_diff
* y_diff
/ dist3
;
257 dE_dy_dx
+= k_mi
* l_mi
* x_diff
* y_diff
/ dist3
;
258 dE_dy_dy
+= k_mi
* (1 - (l_mi
* x_diff
* x_diff
)/dist3
);
262 // Solve for delta_x and delta_y
263 double dE_dx
= partialDer
[best_m
].x1();
264 double dE_dy
= partialDer
[best_m
].x2();
267 (dE_dx_dy
* dE_dy
- dE_dy_dy
* dE_dx
)
268 / (dE_dx_dx
* dE_dy_dy
- dE_dx_dy
* dE_dy_dx
);
271 (dE_dx_dx
* dE_dy
- dE_dy_dx
* dE_dx
)
272 / (dE_dy_dx
* dE_dx_dy
- dE_dx_dx
* dE_dy_dy
);
275 // Move p by (delta_x, delta_y)
277 // cout <<" x"<< delta_x<<"\n";
278 // cout <<" y" <<delta_y<<"\n";
280 GA
.x(best_m
) += delta_x
;
281 GA
.y(best_m
) += delta_y
;
283 // Recompute partial derivatives and delta_p
284 dpair deriv
= computeParDers(best_m
, GA
, sstrength
, oLength
);
285 partialDer
[best_m
] = deriv
;
288 sqrt(deriv
.x1()*deriv
.x1() + deriv
.x2()*deriv
.x2());
289 } while (localItCount
-- > 0 && !finishedNode(delta_m
));
291 // Select new best_m by updating each partial derivative and delta
295 dpair old_deriv_p
= p_partials
[v
];
296 dpair old_p_partial
=
297 computeParDer(v
, old_p
, GA
, sstrength
, oLength
);
298 dpair deriv
= partialDer
[v
];
300 deriv
.x1() += old_p_partial
.x1() - old_deriv_p
.x1();
301 deriv
.x2() += old_p_partial
.x2() - old_deriv_p
.x2();
303 partialDer
[v
] = deriv
;
304 double delta
= sqrt(deriv
.x1()*deriv
.x1() + deriv
.x2()*deriv
.x2());
306 if (delta
> delta_m
) {
313 // cout << "NodeCount: "<<nodeCount<<"\n";
314 // forall_nodes(v, G)
316 // cout<<"Counts "<<v->index()<<": "<<nodeCounts[v]<<"\n";
322 void SpringEmbedderKK::doCall(GraphAttributes
& GA
, const EdgeArray
<double>& eLength
, bool simpleBFS
)
324 const Graph
& G
= GA
.constGraph();
325 NodeArray
<dpair
> partialDer(G
); //stores the partial derivative per node
326 double maxDist
; //maximum distance between nodes
327 NodeArray
< NodeArray
<double> > oLength(G
);//first distance, then original length
328 NodeArray
< NodeArray
<double> > sstrength(G
);//the spring strength
331 OGDF_ASSERT(isConnected(G
));
333 //compute relevant values
334 initialize(GA
, partialDer
, eLength
, oLength
, sstrength
, maxDist
, simpleBFS
);
336 //main loop with node movement
337 mainStep(GA
, partialDer
, oLength
, sstrength
, maxDist
);
339 if (simpleBFS
) scale(GA
);
343 void SpringEmbedderKK::call(GraphAttributes
& GA
)
345 const Graph
&G
= GA
.constGraph();
346 if(G
.numberOfEdges() < 1)
349 EdgeArray
<double> eLength(G
);//, 1.0);is not used
350 doCall(GA
, eLength
, true);
353 void SpringEmbedderKK::call(GraphAttributes
& GA
, const EdgeArray
<double>& eLength
)
355 const Graph
&G
= GA
.constGraph();
356 if(G
.numberOfEdges() < 1)
359 doCall(GA
, eLength
, false);
360 }//call with edge lengths
363 //changes given edge lengths (interpreted as weight factors)
364 //according to additional parameters like node size etc.
365 void SpringEmbedderKK::adaptLengths(
367 const GraphAttributes
& GA
,
368 const EdgeArray
<double>& eLengths
,
369 EdgeArray
<double>& adaptedLengths
)
371 //we use the edge lengths as factor and try to respect
372 //the node sizes such that each node has enough distance
374 //adapt to node sizes
377 double smax
= max(GA
.width(e
->source()), GA
.height(e
->source()));
378 double tmax
= max(GA
.width(e
->target()), GA
.height(e
->target()));
380 adaptedLengths
[e
] = (1+eLengths
[e
])*((smax
+tmax
));///2.0);
381 else adaptedLengths
[e
] = 5.0*eLengths
[e
];
385 void SpringEmbedderKK::shufflePositions(GraphAttributes
& GA
)
387 //first check if degenerated or
388 //just position all on a circle or random layout?
393 // Compute contribution of vertex u to the first partial
394 // derivatives (dE/dx_m, dE/dy_m) (for vertex m) (eq. 7 and 8 in paper)
395 SpringEmbedderKK::dpair
SpringEmbedderKK::computeParDer(
399 NodeArray
< NodeArray
<double> >& ss
,
400 NodeArray
< NodeArray
<double> >& dist
)
402 dpair
result(0.0, 0.0);
405 double x_diff
= GA
.x(m
) - GA
.x(u
);
406 double y_diff
= GA
.y(m
) - GA
.y(u
);
407 double distance
= sqrt(x_diff
* x_diff
+ y_diff
* y_diff
);
408 result
.x1() = (ss
[m
][u
]) * (x_diff
- (dist
[m
][u
])*x_diff
/distance
);
409 result
.x2() = (ss
[m
][u
]) * (y_diff
- (dist
[m
][u
])*y_diff
/distance
);
416 //compute partial derivative for v
417 SpringEmbedderKK::dpair
SpringEmbedderKK::computeParDers(node v
,
419 NodeArray
< NodeArray
<double> >& ss
,
420 NodeArray
< NodeArray
<double> >& dist
)
423 dpair
result(0.0, 0.0);
424 forall_nodes(u
, GA
.constGraph())
426 dpair deriv
= computeParDer(v
, u
, GA
, ss
, dist
);
427 result
.x1() += deriv
.x1();
428 result
.x2() += deriv
.x2();
436 * Initialise the original estimates from nodes and edges.
439 //we could speed this up by not using nested NodeArrays and
440 //by not doing the fully symmetrical computation on undirected graphs
441 //All Pairs Shortest Path Floyd, initializes the whole matrix
442 //returns maximum distance. Does not detect negative cycles (lead to neg. values on diagonal)
443 //threshold is the value for the distance of non-adjacent nodes, distance has to be
445 double SpringEmbedderKK::allpairssp(const Graph
& G
, const EdgeArray
<double>& eLengths
, NodeArray
< NodeArray
<double> >& distance
,
446 const double threshold
)
450 double maxDist
= -threshold
;
454 distance
[v
][v
] = 0.0f
;
457 //TODO: Experimentally compare this against
458 // all nodes and incident edges (memory access) on huge graphs
461 distance
[e
->source()][e
->target()] = eLengths
[e
];
462 distance
[e
->target()][e
->source()] = eLengths
[e
];
466 // * And run the main loop of the algorithm.
475 if ((distance
[u
][v
] < threshold
) && (distance
[v
][w
] < threshold
))
477 distance
[u
][w
] = min( distance
[u
][w
], distance
[u
][v
] + distance
[v
][w
] );
478 //distance[w][u] = distance[u][w]; //is done anyway afterwards
480 if (distance
[u
][w
] < threshold
)
481 maxDist
= max(maxDist
,distance
[u
][w
]);
487 // forall_nodes(v, G)
489 // if (distance[v][v] < 0.0) cerr << "\n###Error in shortest path computation###\n\n";
491 // cout << "Maxdist: "<<maxDist<<"\n";
492 // forall_nodes(u, G)
494 // forall_nodes(w, G)
496 //// cout << "Distance " << u->index() << " -> "<<w->index()<<" "<<distance[u][w]<<"\n";
504 //the same without weights, i.e. all pairs shortest paths with BFS
506 //for compatibility, distances are double
507 double SpringEmbedderKK::allpairsspBFS(const Graph
& G
, NodeArray
< NodeArray
<double> >& distance
)
514 distance
[v
][v
] = 0.0f
;
519 //start in each node once
523 NodeArray
<bool> mark(G
, true);
530 node w
= bfs
.popFrontRet();
532 double d
= distance
[v
][w
]+1.0f
;
533 forall_adj_edges(e
,w
)
535 node u
= e
->opposite(w
);
541 maxDist
= max(maxDist
,d
);
548 //check for negative cycles
551 if (distance
[v
][v
] < 0.0) cerr
<< "\n###Error in shortest path computation###\n\n";
557 // cout << "Maxdist: "<<maxDist<<"\n";
558 // forall_nodes(u, G)
560 // forall_nodes(w, G)
562 //// cout << "Distance " << u->index() << " -> "<<w->index()<<" "<<distance[u][w]<<"\n";
570 void SpringEmbedderKK::scale(GraphAttributes
& GA
)
572 //Simple version: Just scale to max needed
573 //We run over all edges, find the largest distance needed and scale
574 //the edges uniformly
579 forall_edges(e
, GA
.constGraph())
581 double w1
= sqrt(GA
.width(e
->source())*GA
.width(e
->source())+
582 GA
.height(e
->source())*GA
.height(e
->source()));
583 double w2
= sqrt(GA
.width(e
->target())*GA
.width(e
->target())+
584 GA
.height(e
->target())*GA
.height(e
->target()));
585 w2
= (w1
+w2
)/2.0; //half length of both diagonals
586 double xs
= GA
.x(e
->source());
587 double xt
= GA
.x(e
->target());
588 double ys
= GA
.y(e
->source());
589 double yt
= GA
.y(e
->target());
590 double xdist
= xs
-xt
;
591 double ydist
= ys
-yt
;
592 if ((fabs(xs
) > (DBL_MAX
/ 2.0)-1) || (fabs(xt
)> (DBL_MAX
/2.0)-1) ||
593 (fabs(ys
)> (DBL_MAX
/2.0)-1) || (fabs(yt
)> (DBL_MAX
/2.0)-1))
594 scale
= false; //never scale with huge numbers
595 //(even though the drawing may be small and could be shifted to origin)
596 double elength
= sqrt(xdist
*xdist
+ydist
*ydist
);
598 //Avoid a max factor of inf!!
599 if (DIsGreater(elength
, 0.0001))
601 w2
= m_distFactor
* w2
/ elength
;//relative to edge length
609 if (maxFac
> 1.0 && (maxFac
< (DBL_MAX
/2.0)-1) && scale
) //only scale to increase distance
611 //if maxFac is large, we scale in steps until we reach a threshold
614 double scaleF
= maxFac
+0.00001;
618 while (scale
&& maxFac
<scaleF
)
620 forall_nodes(v
, GA
.constGraph())
622 GA
.x(v
) = GA
.x(v
)*base
;
623 GA
.y(v
) = GA
.y(v
)*base
;
624 if (GA
.x(v
) > (DBL_MAX
/ base
)-1 || GA
.y(v
) > (DBL_MAX
/ base
) - 1)
632 forall_nodes(v
, GA
.constGraph())
634 GA
.x(v
) = GA
.x(v
)*maxFac
;
635 GA
.y(v
) = GA
.y(v
)*maxFac
;
639 // cout << "Scaled by factor "<<maxFac<<"\n";