6 * $Date: 2012-07-05 16:45:20 +0200 (Do, 05. Jul 2012) $
7 ***************************************************************/
10 * \brief Implementation of Spring-Embedder algorithm
12 * \author Carsten Gutwenger
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/SpringEmbedderFR.h>
44 #include <ogdf/packing/TileToRowsCCPacker.h>
45 #include <ogdf/basic/GraphCopyAttributes.h>
46 #include <ogdf/basic/simple_graph_alg.h>
53 SpringEmbedderFR::SpringEmbedderFR()
60 m_xleft
= m_ysmall
= 0.0;
61 m_xright
= m_ybig
= 250.0;
64 m_scaling
= scScaleFunction
;
76 void SpringEmbedderFR::call(GraphAttributes
&AG
)
78 const Graph
&G
= AG
.constGraph();
82 // all edges straight-line
88 // compute connected component of G
89 NodeArray
<int> component(G
);
90 int numCC
= connectedComponents(G
,component
);
92 // intialize the array of lists of nodes contained in a CC
93 Array
<List
<node
> > nodesInCC(numCC
);
97 nodesInCC
[component
[v
]].pushBack(v
);
99 EdgeArray
<edge
> auxCopy(G
);
100 Array
<DPoint
> boundingBox(numCC
);
103 for(i
= 0; i
< numCC
; ++i
)
105 GC
.initByNodes(nodesInCC
[i
],auxCopy
);
107 GraphCopyAttributes
AGC(GC
,AG
);
109 forall_nodes(vCopy
, GC
) {
110 node vOrig
= GC
.original(vCopy
);
111 AGC
.x(vCopy
) = AG
.x(vOrig
);
112 AGC
.y(vCopy
) = AG
.y(vOrig
);
116 if (initialize(GC
, AGC
) == true)
118 for(int i
= 1; i
<= m_iterations
; i
++)
125 node vFirst
= GC
.firstNode();
126 double minX
= AGC
.x(vFirst
), maxX
= AGC
.x(vFirst
),
127 minY
= AGC
.y(vFirst
), maxY
= AGC
.y(vFirst
);
129 forall_nodes(vCopy
,GC
) {
130 node v
= GC
.original(vCopy
);
131 AG
.x(v
) = AGC
.x(vCopy
);
132 AG
.y(v
) = AGC
.y(vCopy
);
134 if(AG
.x(v
)-AG
.width (v
)/2 < minX
) minX
= AG
.x(v
)-AG
.width(v
) /2;
135 if(AG
.x(v
)+AG
.width (v
)/2 > maxX
) maxX
= AG
.x(v
)+AG
.width(v
) /2;
136 if(AG
.y(v
)-AG
.height(v
)/2 < minY
) minY
= AG
.y(v
)-AG
.height(v
)/2;
137 if(AG
.y(v
)+AG
.height(v
)/2 > maxY
) maxY
= AG
.y(v
)+AG
.height(v
)/2;
143 forall_nodes(vCopy
,GC
) {
144 node v
= GC
.original(vCopy
);
149 boundingBox
[i
] = DPoint(maxX
- minX
, maxY
- minY
);
152 Array
<DPoint
> offset(numCC
);
153 TileToRowsCCPacker packer
;
154 packer
.call(boundingBox
,offset
,m_pageRatio
);
156 // The arrangement is given by offset to the origin of the coordinate
157 // system. We still have to shift each node and edge by the offset
158 // of its connected component.
160 for(i
= 0; i
< numCC
; ++i
)
162 const List
<node
> &nodes
= nodesInCC
[i
];
164 const double dx
= offset
[i
].m_x
;
165 const double dy
= offset
[i
].m_y
;
167 // iterate over all nodes in ith CC
168 ListConstIterator
<node
> it
;
169 for(it
= nodes
.begin(); it
.valid(); ++it
)
182 bool SpringEmbedderFR::initialize(GraphCopy
&G
, GraphCopyAttributes
&AG
)
184 if(G
.numberOfNodes() <= 1)
185 return false; // nothing to do
189 // compute a suitable area (xleft,ysmall), (xright,ybig)
190 // zoom the current layout into that area
192 double w_sum
= 0.0, h_sum
= 0.0;
193 double xmin
, xmax
, ymin
, ymax
;
195 node v
= G
.firstNode();
196 xmin
= xmax
= AG
.x(v
);
197 ymin
= ymax
= AG
.y(v
);
200 if(AG
.x(v
) < xmin
) xmin
= AG
.x(v
);
201 if(AG
.x(v
) > xmax
) xmax
= AG
.x(v
);
202 if(AG
.y(v
) < ymin
) ymin
= AG
.y(v
);
203 if(AG
.y(v
) > ymax
) ymax
= AG
.y(v
);
204 w_sum
+= AG
.getWidth (v
);
205 h_sum
+= AG
.getHeight(v
);
216 case scUserBoundingBox
:
217 case scScaleFunction
:
219 if (m_scaling
== scUserBoundingBox
) {
226 double sqrt_n
= sqrt((double)G
.numberOfNodes());
229 m_xright
= (w_sum
> 0) ? m_scaleFactor
* w_sum
/ sqrt_n
: 1;
230 m_ybig
= (h_sum
> 0) ? m_scaleFactor
* h_sum
/ sqrt_n
: 1;
232 // Compute scaling such that layout coordinates fit into used bounding box
233 double fx
= (xmax
== xmin
) ? 1.0 : m_xright
/ (xmax
- xmin
);
234 double fy
= (ymax
== ymin
) ? 1.0 : m_ybig
/ (ymax
- ymin
);
235 // Adjust coordinates accordingly
237 AG
.x(v
) = m_xleft
+ (AG
.x(v
) - xmin
) * fx
;
238 AG
.y(v
) = m_ysmall
+ (AG
.y(v
) - ymin
) * fy
;
246 m_width
= m_xright
- m_xleft
;
247 m_height
= m_ybig
- m_ysmall
;
249 OGDF_ASSERT((m_width
>= 0) && (m_height
>= 0))
251 m_txNull
= m_width
/50;
252 m_tyNull
= m_height
/50;
256 //m_k = sqrt(m_width*m_height / G.numberOfNodes()) / 2;
257 m_k
= m_fineness
* sqrt(m_width
*m_height
/ G
.numberOfNodes());
263 if (m_ki
== 0) m_ki
= 1;
267 // build matrix of node lists
268 m_xA
= int(m_width
/ m_ki
+ 1);
269 m_yA
= int(m_height
/ m_ki
+ 1);
270 m_A
= new Array2D
<List
<node
> >(-1,m_xA
,-1,m_yA
);
277 int i
= int((xv
- m_xleft
) / m_ki
);
278 int j
= int((yv
- m_ysmall
) / m_ki
);
280 OGDF_ASSERT( (i
< m_xA
) && (i
> -1) )
281 OGDF_ASSERT( (j
< m_yA
) && (j
> -1) )
283 m_lit
[v
] = (*m_A
)(i
,j
).pushFront(v
);
290 #define FREPULSE(d) ((m_k2 > (d)) ? m_kk/(d) : 0)
293 void SpringEmbedderFR::mainStep(GraphCopy
&G
, GraphCopyAttributes
&AG
)
295 //const Graph &G = AG.constGraph();
300 NodeArray
<double> xdisp(G
,0);
301 NodeArray
<double> ydisp(G
,0);
309 int i
= int((xv
- m_xleft
) / m_ki
);
310 int j
= int((yv
- m_ysmall
) / m_ki
);
312 for(int m
= -1; m
<= 1; m
++)
314 for(int n
= -1; n
<= 1; n
++)
316 ListIterator
<node
> it
;
317 for(it
= (*m_A
)(i
+m
,j
+n
).begin(); it
.valid(); ++it
)
322 double xdist
= xv
- AG
.x(u
);
323 double ydist
= yv
- AG
.y(u
);
324 double dist
= sqrt(xdist
*xdist
+ ydist
*ydist
);
327 xdisp
[v
] += FREPULSE(dist
) * xdist
/ dist
;
328 ydisp
[v
] += FREPULSE(dist
) * ydist
/ dist
;
337 node u
= e
->source();
338 node v
= e
->target();
339 double xdist
= AG
.x(v
) - AG
.x(u
);
340 double ydist
= AG
.y(v
) - AG
.y(u
);
341 double dist
= sqrt(xdist
*xdist
+ ydist
*ydist
);
343 double f
= (u
->degree()+v
->degree())/6.0;
347 double fac
= dist
/ m_k
;
349 xdisp
[v
] -= xdist
*fac
;
350 ydisp
[v
] -= ydist
*fac
;
351 xdisp
[u
] += xdist
*fac
;
352 ydisp
[u
] += ydist
*fac
;
360 xdisp
[v
] *= (double(randomNumber(750,1250))/1000.0);
361 ydisp
[v
] *= (double(randomNumber(750,1250))/1000.0);
373 int i0
= int((xv
- m_xleft
) / m_ki
);
374 int j0
= int((yv
- m_ysmall
) / m_ki
);
376 double xd
= xdisp
[v
];
377 double yd
= ydisp
[v
];
378 double dist
= sqrt(xd
*xd
+yd
*yd
);
391 if( (xp
> m_xleft
) && (xp
< m_xright
) )
394 i
= int((xp
- m_xleft
) / m_ki
);
398 if( (yp
> m_ysmall
) && (yp
< m_ybig
) )
401 j
= int((yp
- m_ysmall
) / m_ki
);
405 if( (i
!= i0
) || (j
!= j0
) )
407 OGDF_ASSERT(m_lit
[v
].valid());
409 (*m_A
)(i0
,j0
).moveToFront(m_lit
[v
], (*m_A
)(i
,j
));
413 m_tx
= m_txNull
/ mylog2(m_cF
);
414 m_ty
= m_tyNull
/ mylog2(m_cF
);
420 } // end namespace ogdf