6 * $Date: 2012-07-15 22:39:24 +0200 (So, 15. Jul 2012) $
7 ***************************************************************/
10 * \brief Definition of the Schnyder Layout Algorithm (SchnyderLayout)
12 * \author Till Schäfer
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/planarlayout/SchnyderLayout.h>
44 #include <ogdf/basic/extended_graph_alg.h>
45 #include <ogdf/basic/simple_graph_alg.h>
46 #include <ogdf/basic/GraphCopy.h>
47 #include <ogdf/basic/List.h>
51 SchnyderLayout::SchnyderLayout() : PlanarGridLayoutModule() {
55 void SchnyderLayout::doCall(
58 GridLayout
&gridLayout
,
62 // check for double edges & self loops
63 OGDF_ASSERT(isSimple(G
));
65 // handle special case of graphs with less than 3 nodes
66 if (G
.numberOfNodes() < 3) {
68 switch (G
.numberOfNodes()) {
70 boundingBox
= IPoint(0, 0);
75 gridLayout
.x(v1
) = gridLayout
.y(v1
) = 0;
76 boundingBox
= IPoint(0, 0);
82 gridLayout
.x(v1
) = gridLayout
.y(v1
) = gridLayout
.y(v2
) = 0;
84 boundingBox
= IPoint(1, 0);
89 // make a copy for triangulation
94 if (planarEmbed(GC
) == false) {
95 OGDF_THROW_PARAM(PreconditionViolatedException
, pvcPlanar
);
101 schnyderEmbedding(GC
, gridLayout
, adjExternal
);
105 void SchnyderLayout::schnyderEmbedding(
107 GridLayout
&gridLayout
,
108 adjEntry adjExternal
)
110 NodeArray
<int> &xcoord
= gridLayout
.x();
111 NodeArray
<int> &ycoord
= gridLayout
.y();
114 List
<node
> L
; // (un)contraction order
115 GraphCopy T
= GraphCopy(GC
); // the realizer tree (reverse direction of edges!!!)
116 EdgeArray
<int> rValues(T
); // the realizer values
118 // choose outer face a,b,c
120 if (adjExternal
!= 0) {
121 edge eG
= adjExternal
->theEdge();
122 edge eGC
= GC
.copy(eG
);
123 adja
= (adjExternal
== eG
->adjSource()) ? eGC
->adjSource() : eGC
->adjTarget();
126 adja
= GC
.firstEdge()->adjSource();
128 adjEntry adjb
= adja
->faceCyclePred();
129 adjEntry adjc
= adjb
->faceCyclePred();
131 node a
= adja
->theNode();
132 node b
= adjb
->theNode();
133 node c
= adjc
->theNode();
135 node a_in_T
= T
.copy(GC
.original(a
));
136 node b_in_T
= T
.copy(GC
.original(b
));
137 node c_in_T
= T
.copy(GC
.original(c
));
139 contract(GC
, a
, b
, c
, L
);
141 realizer(GC
, L
, a
, b
, c
, rValues
, T
);
145 GraphAttributes GA(T,GraphAttributes::edgeLabel | GraphAttributes::edgeArrow);
146 forall_edges(ex, T) {
148 int n = sprintf(buffer, 10, "%d", rValues[ex]);
149 GA.labelEdge(ex) = buffer;
150 GA.arrowEdge(ex) = GraphAttributes::last;
153 GC.writeGML("/home/till/temp/gml/after_realizer_gc.gml");
154 GA.writeGML("/home/till/temp/gml/tree.gml");*/
156 NodeArray
<int> t1(T
);
157 NodeArray
<int> t2(T
);
158 NodeArray
<int> val(T
, 1);
160 NodeArray
<int> P1(T
);
161 NodeArray
<int> P3(T
);
162 NodeArray
<int> v1(T
);
163 NodeArray
<int> v2(T
);
165 subtreeSizes(rValues
, 1, a_in_T
, t1
);
166 subtreeSizes(rValues
, 2, b_in_T
, t2
);
168 prefixSum(rValues
, 1, a_in_T
, val
, P1
);
169 prefixSum(rValues
, 3, c_in_T
, val
, P3
);
170 // now Pi = depth of all nodes in Tree T(i) (depth[root] = 1)
172 prefixSum(rValues
, 2, b_in_T
, t1
, v1
);
173 // special treatment for a
174 v1
[a_in_T
] = t1
[a_in_T
];
177 * v1[v] now is the sum of the
178 * "count of nodes in t1" minus the "subtree size for node x"
179 * for every node x on a path from b to v in t2
182 prefixSum(rValues
, 3, c_in_T
, t1
, val
);
183 // special treatment for a
184 val
[a_in_T
] = t1
[a_in_T
];
187 * val[v] now is the sum of the
188 * "count of nodes in t1" minus the "subtree size for node x"
189 * for every node x on a path from c to v in t3
192 // r1[v]=v1[v]+val[v]-t1[v] is the number of nodes in region 1 from v
195 v1
[v
] += val
[v
] - t1
[v
] - P3
[v
];
198 prefixSum(rValues
, 3, c_in_T
, t2
, v2
);
199 // special treatment for b
200 v2
[b_in_T
] = t2
[b_in_T
];
202 prefixSum(rValues
, 1, a_in_T
, t2
, val
);
203 // special treatment for b
204 val
[b_in_T
] = t2
[b_in_T
];
208 v2
[v
] += val
[v
] - t2
[v
] - P1
[v
];
211 // copy coordinates to the GridLayout
212 forall_nodes(v
, GC
) {
213 xcoord
[GC
.original(v
)] = v1
[T
.copy(GC
.original(v
))];
214 ycoord
[GC
.original(v
)] = v2
[T
.copy(GC
.original(v
))];
221 * L is the ordering for uncontracting the nodes in realizer
223 void SchnyderLayout::contract(Graph
& G
, node a
, node b
, node c
, List
<node
>& L
)
226 List
<node
> candidates
;
227 NodeArray
<bool> marked(G
, false); // considered nodes
228 NodeArray
<int> deg(G
, 0); // # virtual neighbours
230 int N
= G
.numberOfEdges();
232 marked
[a
] = marked
[b
] = marked
[c
] = true; // init outer face
234 deg
[a
] = deg
[b
] = deg
[c
] = N
;
236 // mark neighbours of a and calc the degree of the second (virtual) neighbours
237 forall_adj(adj1
, a
) {
238 marked
[adj1
->twinNode()] = true;
239 forall_adj(adj2
, adj1
->twinNode()) {
240 deg
[adj2
->twinNode()]++;
244 // find first candidates
245 forall_adj(adj1
, a
) {
246 if (deg
[adj1
->twinNode()] <= 2) {
247 candidates
.pushBack(adj1
->twinNode());
251 while (!candidates
.empty()) {
252 node u
= candidates
.popFrontRet();
256 forall_adj(adj1
, u
) {
257 node v
= adj1
->twinNode();
258 deg
[v
]--; // u is virtualy deleted
259 if (!marked
[v
]) { // v is new neighbour of a
261 forall_adj(adj2
, v
) {
262 deg
[adj2
->twinNode()]++; // degree of virtaul neighbours increase
264 if (deg
[v
] <= 2) candidates
.pushBack(v
); // next candidate v
267 if (deg
[v
] == 2) candidates
.pushBack(v
); // next candidate v
275 * Construct the realiszer and the Tree T
276 * rValues = realizer value
279 void SchnyderLayout::realizer(
285 EdgeArray
<int>& rValues
,
290 NodeArray
<int> ord(G
, 0);
297 forall_listiterators(node
, it
, L
) {
298 ord
[*it
] = i
++; // enumerate V(G)
302 // remove all edges (they will be re-added later with different orientation)
303 while (T
.numberOfEdges() > 0) {
308 forall_listiterators(node
, it
, L
) {
310 node u
= T
.copy(G
.original(v
)); // u is copy of v in T
313 if (ord
[adj
->twinNode()] > ord
[v
]) {
319 while (ord
[adj1
->twinNode()] > ord
[v
]) {
320 adj1
= adj1
->cyclicSucc();
322 e
= T
.newEdge(T
.copy(G
.original(adj1
->twinNode())), u
);
326 while (ord
[adj2
->twinNode()] > ord
[v
]) {
327 adj2
= adj2
->cyclicPred();
329 e
= T
.newEdge(T
.copy(G
.original(adj2
->twinNode())), u
);
332 for (adj
= adj1
->cyclicSucc(); adj
!= adj2
; adj
= adj
->cyclicSucc()) {
333 e
= T
.newEdge(u
, T
.copy(G
.original(adj
->twinNode())));
338 // special treatement of a,b,c
339 node a_in_T
= T
.copy(G
.original(a
));
340 node b_in_T
= T
.copy(G
.original(b
));
341 node c_in_T
= T
.copy(G
.original(c
));
343 // all edges to node a get realizer value 1
345 e
= T
.newEdge(a_in_T
, T
.copy(G
.original(adj
->twinNode())));
349 // rest of outer triangle (reciprocal linked, realizer values 2 and 3)
350 e
= T
.newEdge(b_in_T
, a_in_T
);
352 e
= T
.newEdge(b_in_T
, c_in_T
);
355 e
= T
.newEdge(c_in_T
, a_in_T
);
357 e
= T
.newEdge(c_in_T
, b_in_T
);
363 * computes the sizes of all subtrees of a tree with root r
365 void SchnyderLayout::subtreeSizes(
366 EdgeArray
<int>& rValues
,
369 NodeArray
<int>& size
)
374 if (adj
->theEdge()->source() == r
&& rValues
[adj
->theEdge()] == i
) {
375 node w
= adj
->twinNode();
376 subtreeSizes(rValues
, i
, w
, size
);
384 * computes for every node u in the subtree of T(i) with root r
385 * the sum of all val[v] where v is a node on the path from r to u
387 void SchnyderLayout::prefixSum(
388 EdgeArray
<int>& rValues
,
391 const NodeArray
<int>& val
,
400 node v
= Q
.popFrontRet();
403 if (adj
->theEdge()->source() == v
&& rValues
[adj
->theEdge()] == i
) {
404 node w
= adj
->twinNode();
406 sum
[w
] = val
[w
] + sum
[v
];