Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/planarity/graphDrawPlanar.c
4069 views
1
/*
2
Planarity-Related Graph Algorithms Project
3
Copyright (c) 1997-2010, John M. Boyer
4
All rights reserved. Includes a reference implementation of the following:
5
6
* John M. Boyer. "Simplified O(n) Algorithms for Planar Graph Embedding,
7
Kuratowski Subgraph Isolation, and Related Problems". Ph.D. Dissertation,
8
University of Victoria, 2001.
9
10
* John M. Boyer and Wendy J. Myrvold. "On the Cutting Edge: Simplified O(n)
11
Planarity by Edge Addition". Journal of Graph Algorithms and Applications,
12
Vol. 8, No. 3, pp. 241-273, 2004.
13
14
* John M. Boyer. "A New Method for Efficiently Generating Planar Graph
15
Visibility Representations". In P. Eades and P. Healy, editors,
16
Proceedings of the 13th International Conference on Graph Drawing 2005,
17
Lecture Notes Comput. Sci., Volume 3843, pp. 508-511, Springer-Verlag, 2006.
18
19
Redistribution and use in source and binary forms, with or without modification,
20
are permitted provided that the following conditions are met:
21
22
* Redistributions of source code must retain the above copyright notice, this
23
list of conditions and the following disclaimer.
24
25
* Redistributions in binary form must reproduce the above copyright notice, this
26
list of conditions and the following disclaimer in the documentation and/or
27
other materials provided with the distribution.
28
29
* Neither the name of the Planarity-Related Graph Algorithms Project nor the names
30
of its contributors may be used to endorse or promote products derived from this
31
software without specific prior written permission.
32
33
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
37
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
40
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
41
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
*/
44
45
#include "graphDrawPlanar.h"
46
#include "graphDrawPlanar.private.h"
47
48
extern int DRAWPLANAR_ID;
49
50
#include "graph.h"
51
52
#if 0
53
#include <malloc.h>
54
#else
55
#if !defined(_XOPEN_SOURCE) && !defined(_ISOC99_SOURCE)
56
#define _XOPEN_SOURCE 600
57
#endif
58
#include <stdlib.h> /* ISO C99 also defines malloc() to be there. */
59
#endif
60
#include <string.h>
61
#include <stdio.h>
62
63
extern void _FillVisitedFlags(graphP theGraph, int FillValue);
64
65
/* Private functions exported to system */
66
67
void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink);
68
int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink);
69
70
int _ComputeVisibilityRepresentation(DrawPlanarContext *context);
71
int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context);
72
73
/* Private functions */
74
int _ComputeVertexPositions(DrawPlanarContext *context);
75
int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pIndex);
76
int _ComputeEdgePositions(DrawPlanarContext *context);
77
int _ComputeVertexRanges(DrawPlanarContext *context);
78
int _ComputeEdgeRanges(DrawPlanarContext *context);
79
80
/********************************************************************
81
_ComputeVisibilityRepresentation()
82
83
Compute vertex positions
84
Compute edge positions
85
Assign horizontal ranges of vertices
86
Assign vertical ranges of edges
87
88
********************************************************************/
89
90
int _ComputeVisibilityRepresentation(DrawPlanarContext *context)
91
{
92
if (sp_NonEmpty(context->theGraph->edgeHoles))
93
return NOTOK;
94
95
if (_ComputeVertexPositions(context) != OK)
96
return NOTOK;
97
98
if (_ComputeEdgePositions(context) != OK)
99
return NOTOK;
100
101
if (_ComputeVertexRanges(context) != OK)
102
return NOTOK;
103
104
if (_ComputeEdgeRanges(context) != OK)
105
return NOTOK;
106
107
return OK;
108
}
109
110
/********************************************************************
111
_ComputeVertexPositions()
112
113
Computes the vertex positions in the graph. This method accounts
114
for disconnected graphs by finding the DFS tree roots and then,
115
for each, invoking _ComputeVertexPositionsInComponent().
116
The index variable for the positioning is maintained by this method
117
so that the vertices in separate components still get distinct
118
vertex positions.
119
********************************************************************/
120
121
int _ComputeVertexPositions(DrawPlanarContext *context)
122
{
123
graphP theEmbedding = context->theGraph;
124
int I, index;
125
126
for (I = index = 0; I < theEmbedding->N; I++)
127
{
128
// For each DFS tree root in the embedding, we
129
// compute the vertex positions
130
if (theEmbedding->V[I].DFSParent == NIL)
131
{
132
if (_ComputeVertexPositionsInComponent(context, I, &index) != OK)
133
return NOTOK;
134
}
135
}
136
137
return OK;
138
}
139
140
/********************************************************************
141
_ComputeVertexPositionsInComponent()
142
143
The vertical positions of the vertices are computed based in part
144
on the information compiled during the planar embedding.
145
146
Each vertex is marked as being between its parent and some ancestor
147
or beyond the parent relative to the ancestor. The localized,
148
intuitive notion is that the vertex is either below the parent
149
or above the parent, but the bicomp containing the vertex, its
150
parent and the ancestor may be turned upside-down as the result
151
of a global sequence of operations, resulting in a between or beyond
152
generalization.
153
154
As the core planarity algorithm constructs successively larger
155
bicomps out of smaller ones, the bicomp root and its DFS child
156
are marked as 'tied' in vertex position using markers along the
157
external face. The marking of the DFS child may be indirect.
158
Since the child may not be on the external face, its descendant
159
that is next along the external face is marked instead.
160
161
Later (possibly in the same step or possibly many vertices later),
162
the Walkdown proceeds around the bicomp and returns to each merge
163
point, and the tie is broken based on the direction of approach.
164
165
As the Walkdown passes a vertex to its successor, the external
166
face is short-circuited to remove the vertex from it. Immediately
167
before this occurs, the new drawing method resolves the tie. Since
168
the vertex is going to the internal face, its vertex position should
169
be 'between' its successor and the current vertex being processed
170
by the Walkdown.
171
172
If the vertex is a child of its external face successor, then it
173
is simply marked as being 'between' that successor and the current
174
vertex being processed by the planarity method. But if the vertex
175
is the parent of its external face successor, then the successor
176
is placed 'beyond' the vertex. Recall that the successor is either
177
the DFS child of the vertex or a descendant of that DFS child that
178
was specially marked because it, not the DFS child, was on the
179
external face.
180
181
This explains the information that has been collected by the
182
planarity embedder, which will now be turned into a vertex ordering
183
system. The idea is to proceed with a pre-order traversal of
184
the DFS tree, determining the relative orders of the ancestors of
185
a vertex by the time we get to a vertex. This will allow us to
186
convert between/beyond into above/below based on the known relative
187
order of the parent and some given ancestor of the vertex. A vertex
188
would then be added immediately above or below its parent in the
189
total ordering, and then the algorithm proceeds to the descendants.
190
191
Consider a depth-first pre-order visitation of vertices. If the
192
full order of all vertices visited so far is dynamically maintained,
193
then it is easy to decide whether a vertex goes above or below
194
its parent based on the between/beyond indicator and the relative
195
positions in the order of the parent and given ancestor of the
196
vertex. If the ancestor is above the parent, then 'between' means
197
put the vertex immediately above its parent and 'beyond' means put
198
the vertex immediately below its parent in the order. And if the
199
ancestor is below the parent, then the meaning of between and
200
beyond are simply reversed.
201
202
Once a vertex is known to be above or below its parent, the drawing
203
flag is changed from between/beyond to above/below, and processing
204
proceeds to the next vertex in pre-order depth first search.
205
206
The difficulty lies in keeping an up-to-date topological ordering
207
that can be queried in constant time to find the relative positions
208
of two vertices. By itself, this is an instance of "online" or
209
dynamic topological sorting and has been proven not to be achievable
210
in linear total time. But this is a special case of the problem and
211
is therefore solvable through the collection and maintenance of some
212
additional information.
213
214
Recall that the ancestor V of a vertex is recorded when the setting
215
for between/beyond is made for a vertex. However, the Walkdown is
216
invoked on the bicomp rooted by edge (V', C), so the child C of V
217
that roots the subtree containing the vertex being marked is known.
218
219
Note that when a DFS child is placed above its parent, the entire
220
DFS subtree of vertices is placed above the parent. Hence, to
221
determine whether the parent P of a vertex W is above the ancestor
222
V, where W is marked either between or beyond P and V, we need
223
only determine the relationship between V and C, which has already
224
been directly determined due to previous steps of the algorithm
225
(because V and C are ancestors of P and W). If C is above/below V
226
then so is P.
227
228
As mentioned above, once the position of P is known relative to V,
229
it is a simple matter to decide whether to put W above or below P
230
based on the between/beyond indicator stored in W during embedding.
231
********************************************************************/
232
233
int _ComputeVertexPositionsInComponent(DrawPlanarContext *context, int root, int *pIndex)
234
{
235
graphP theEmbedding = context->theGraph;
236
listCollectionP theOrder = LCNew(theEmbedding->N);
237
int W, P, C, V, J;
238
239
if (theOrder == NULL)
240
return NOTOK;
241
242
// Determine the vertex order using a depth first search with
243
// pre-order visitation.
244
245
sp_ClearStack(theEmbedding->theStack);
246
sp_Push(theEmbedding->theStack, root);
247
while (!sp_IsEmpty(theEmbedding->theStack))
248
{
249
sp_Pop(theEmbedding->theStack, W);
250
251
P = theEmbedding->V[W].DFSParent;
252
V = context->V[W].ancestor;
253
C = context->V[W].ancestorChild;
254
255
// For the special case that we just popped the DFS tree root,
256
// we simply add the root to its own position.
257
if (P == NIL)
258
{
259
// Put the DFS root in the list by itself
260
LCAppend(theOrder, NIL, W);
261
// The children of the DFS root have the root as their
262
// ancestorChild and 'beyond' as the drawingFlag, so this
263
// causes the root's children to be placed below the root
264
context->V[W].drawingFlag = DRAWINGFLAG_BELOW;
265
}
266
267
// Determine vertex W position relative to P
268
else
269
{
270
// An unresolved tie is an error
271
if (context->V[W].drawingFlag == DRAWINGFLAG_TIE)
272
return NOTOK;
273
274
// If C below V, then P below V, so interpret W between
275
// P and V as W above P, and interpret W beyond P relative
276
// to V as W below P.
277
if (context->V[C].drawingFlag == DRAWINGFLAG_BELOW)
278
{
279
if (context->V[W].drawingFlag == DRAWINGFLAG_BETWEEN)
280
context->V[W].drawingFlag = DRAWINGFLAG_ABOVE;
281
else
282
context->V[W].drawingFlag = DRAWINGFLAG_BELOW;
283
}
284
285
// If C above V, then P above V, so interpret W between
286
// P and V as W below P, and interpret W beyond P relative
287
// to V as W above P.
288
else
289
{
290
if (context->V[W].drawingFlag == DRAWINGFLAG_BETWEEN)
291
context->V[W].drawingFlag = DRAWINGFLAG_BELOW;
292
else
293
context->V[W].drawingFlag = DRAWINGFLAG_ABOVE;
294
}
295
296
if (context->V[W].drawingFlag == DRAWINGFLAG_BELOW)
297
LCInsertAfter(theOrder, P, W);
298
else
299
LCInsertBefore(theOrder, P, W);
300
}
301
302
// Push DFS children
303
J = gp_GetFirstArc(theEmbedding, W);
304
while (gp_IsArc(theEmbedding, J))
305
{
306
if (theEmbedding->G[J].type == EDGE_DFSCHILD)
307
sp_Push(theEmbedding->theStack, theEmbedding->G[J].v);
308
309
J = gp_GetNextArc(theEmbedding, J);
310
}
311
}
312
313
// Use the order to assign vertical positions
314
V = root;
315
while (V != NIL)
316
{
317
context->G[V].pos = *pIndex;
318
(*pIndex)++;
319
V = LCGetNext(theOrder, root, V);
320
}
321
322
// Clean up and return
323
324
LCFree(&theOrder);
325
return OK;
326
}
327
328
329
#ifdef LOGGING
330
/********************************************************************
331
_LogEdgeList()
332
Used to show the progressive calculation of the edge position list.
333
********************************************************************/
334
void _LogEdgeList(graphP theEmbedding, listCollectionP edgeList, int edgeListHead)
335
{
336
int e = edgeListHead, J, JTwin;
337
338
gp_Log("EdgeList: [ ");
339
340
while (e != NIL)
341
{
342
J = theEmbedding->edgeOffset + 2*e;
343
JTwin = gp_GetTwinArc(theEmbedding, J);
344
345
gp_Log(gp_MakeLogStr2("(%d, %d) ",
346
theEmbedding->G[theEmbedding->G[J].v].v,
347
theEmbedding->G[theEmbedding->G[JTwin].v].v));
348
349
e = LCGetNext(edgeList, edgeListHead, e);
350
}
351
352
gp_LogLine("]");
353
}
354
#endif
355
356
/********************************************************************
357
_ComputeEdgePositions()
358
359
Performs a vertical sweep of the combinatorial planar embedding,
360
developing the edge order in the horizontal sweep line as it
361
advances through the vertices according to their assigned
362
vertical positions.
363
364
For expedience, the 'visited' flag for each vertex shall be used
365
instead to indicate the location in the edge order list of the
366
generator edge for the vertex, i.e. the first edge added to the
367
vertex from a higher vertex (with lower position number).
368
All edges added from this vertex to the neighbors below it are
369
added immediately after the generator edge for the vertex.
370
********************************************************************/
371
372
int _ComputeEdgePositions(DrawPlanarContext *context)
373
{
374
graphP theEmbedding = context->theGraph;
375
int *vertexOrder = NULL;
376
listCollectionP edgeList = NULL;
377
int edgeListHead, edgeListInsertPoint;
378
int I, J, Jcur, e, v, vpos;
379
int eIndex, JTwin;
380
381
gp_LogLine("\ngraphDrawPlanar.c/_ComputeEdgePositions() start");
382
383
// Sort the vertices by vertical position (in linear time)
384
385
if ((vertexOrder = (int *) malloc(theEmbedding->N * sizeof(int))) == NULL)
386
return NOTOK;
387
388
for (I = 0; I < theEmbedding->N; I++)
389
vertexOrder[context->G[I].pos] = I;
390
391
// Allocate the edge list of size M.
392
// This is an array of (prev, next) pointers.
393
// An edge at position X corresponds to the edge
394
// at position X in the graph structure, which is
395
// represented by a pair of adjacent graph nodes
396
// starting at index 2N + 2X.
397
398
if (theEmbedding->M > 0 && (edgeList = LCNew(theEmbedding->M)) == NULL)
399
{
400
free(vertexOrder);
401
return NOTOK;
402
}
403
404
edgeListHead = NIL;
405
406
// Each vertex starts out with a NIL generator edge.
407
408
for (I=0; I < theEmbedding->N; I++)
409
theEmbedding->G[I].visited = NIL;
410
411
// Perform the vertical sweep of the combinatorial embedding, using
412
// the vertex ordering to guide the sweep.
413
// For each vertex, each edge leading to a vertex with a higher number in
414
// the vertex order is recorded as the "generator edge", or the edge of
415
// first discovery of that higher numbered vertex, unless the vertex already has
416
// a recorded generator edge
417
for (vpos=0; vpos < theEmbedding->N; vpos++)
418
{
419
// Get the vertex associated with the position
420
v = vertexOrder[vpos];
421
gp_LogLine(gp_MakeLogStr3("Processing vertex %d with DFI=%d at position=%d",
422
theEmbedding->G[v].v, v, vpos));
423
424
// The DFS tree root of a connected component is always the least
425
// number vertex in the vertex ordering. We have to give it a
426
// false generator edge so that it is still "visited" and then
427
// all of its edges are generators for its neighbor vertices because
428
// they all have greater numbers in the vertex order.
429
if (theEmbedding->V[v].DFSParent == NIL)
430
{
431
// False generator edge, so the vertex is distinguishable from
432
// a vertex with no generator edge when its neighbors are visited
433
// This way, an edge from a neighbor won't get recorded as the
434
// generator edge of the DFS tree root.
435
theEmbedding->G[v].visited = 1;
436
437
// Now we traverse the adjacency list of the DFS tree root and
438
// record each edge as the generator edge of the neighbors
439
J = gp_GetFirstArc(theEmbedding, v);
440
while (gp_IsArc(theGraph, J))
441
{
442
e = (J - theEmbedding->edgeOffset) / 2;
443
444
edgeListHead = LCAppend(edgeList, edgeListHead, e);
445
gp_LogLine(gp_MakeLogStr2("Append generator edge (%d, %d) to edgeList",
446
theEmbedding->G[v].v, theEmbedding->G[theEmbedding->G[J].v].v));
447
448
// Set the generator edge for the root's neighbor
449
theEmbedding->G[theEmbedding->G[J].v].visited = J;
450
451
// Go to the next node of the root's adj list
452
J = gp_GetNextArc(theEmbedding, J);
453
}
454
}
455
456
// Else, if we are not on a DFS tree root...
457
else
458
{
459
// Get the generator edge of the vertex
460
if ((JTwin = theEmbedding->G[v].visited) == NIL)
461
return NOTOK;
462
J = gp_GetTwinArc(theEmbedding, JTwin);
463
464
// Traverse the edges of the vertex, starting
465
// from the generator edge and going counterclockwise...
466
467
e = (J - theEmbedding->edgeOffset) / 2;
468
edgeListInsertPoint = e;
469
470
Jcur = gp_GetNextArcCircular(theEmbedding, J);
471
472
while (Jcur != J)
473
{
474
// If the neighboring vertex's position is greater
475
// than the current vertex (meaning it is lower in the
476
// diagram), then add that edge to the edge order.
477
478
if (context->G[theEmbedding->G[Jcur].v].pos > vpos)
479
{
480
e = (Jcur - theEmbedding->edgeOffset) / 2;
481
LCInsertAfter(edgeList, edgeListInsertPoint, e);
482
483
gp_LogLine(gp_MakeLogStr4("Insert (%d, %d) after (%d, %d)",
484
theEmbedding->G[v].v,
485
theEmbedding->G[theEmbedding->G[Jcur].v].v,
486
theEmbedding->G[theEmbedding->G[gp_GetTwinArc(theEmbedding, J)].v].v,
487
theEmbedding->G[theEmbedding->G[J].v].v));
488
489
edgeListInsertPoint = e;
490
491
// If the vertex does not yet have a generator edge, then set it.
492
if (theEmbedding->G[theEmbedding->G[Jcur].v].visited == NIL)
493
{
494
theEmbedding->G[theEmbedding->G[Jcur].v].visited = Jcur;
495
gp_LogLine(gp_MakeLogStr2("Generator edge (%d, %d)",
496
theEmbedding->G[theEmbedding->G[gp_GetTwinArc(theEmbedding, J)].v].v,
497
theEmbedding->G[theEmbedding->G[Jcur].v].v));
498
}
499
}
500
501
// Go to the next node in v's adjacency list
502
Jcur = gp_GetNextArcCircular(theEmbedding, Jcur);
503
}
504
}
505
506
#ifdef LOGGING
507
_LogEdgeList(theEmbedding, edgeList, edgeListHead);
508
#endif
509
}
510
511
// Now iterate through the edgeList and assign positions to the edges.
512
eIndex = 0;
513
e = edgeListHead;
514
while (e != NIL)
515
{
516
J = theEmbedding->edgeOffset + 2*e;
517
JTwin = gp_GetTwinArc(theEmbedding, J);
518
519
context->G[J].pos = context->G[JTwin].pos = eIndex;
520
521
eIndex++;
522
523
e = LCGetNext(edgeList, edgeListHead, e);
524
}
525
526
// Clean up and return
527
LCFree(&edgeList);
528
free(vertexOrder);
529
530
gp_LogLine("graphDrawPlanar.c/_ComputeEdgePositions() end\n");
531
532
return OK;
533
}
534
535
/********************************************************************
536
_ComputeVertexRanges()
537
538
Assumes edge positions are known (see _ComputeEdgePositions()).
539
A vertex spans horizontally the positions of the edges incident
540
to it.
541
********************************************************************/
542
543
int _ComputeVertexRanges(DrawPlanarContext *context)
544
{
545
graphP theEmbedding = context->theGraph;
546
int I, J, min, max;
547
548
for (I = 0; I < theEmbedding->N; I++)
549
{
550
min = theEmbedding->M + 1;
551
max = -1;
552
553
// Iterate the edges, except in the isolated vertex case we just
554
// set the min and max to 1 since there no edges controlling where
555
// it gets drawn.
556
J = gp_GetFirstArc(theEmbedding, I);
557
if (!gp_IsArc(theEmbedding, J))
558
{
559
min = max = 0;
560
}
561
else
562
{
563
while (gp_IsArc(theEmbedding, J))
564
{
565
if (min > context->G[J].pos)
566
min = context->G[J].pos;
567
568
if (max < context->G[J].pos)
569
max = context->G[J].pos;
570
571
J = gp_GetNextArc(theEmbedding, J);
572
}
573
}
574
575
context->G[I].start = min;
576
context->G[I].end = max;
577
}
578
579
return OK;
580
}
581
582
/********************************************************************
583
_ComputeEdgeRanges()
584
585
Assumes vertex positions are known (see _ComputeVertexPositions()).
586
An edges spans the vertical range of its endpoints.
587
********************************************************************/
588
589
int _ComputeEdgeRanges(DrawPlanarContext *context)
590
{
591
graphP theEmbedding = context->theGraph;
592
int e, J, JTwin, v1, v2, pos1, pos2;
593
594
for (e = 0; e < theEmbedding->M; e++)
595
{
596
J = theEmbedding->edgeOffset + 2*e;
597
JTwin = gp_GetTwinArc(theEmbedding, J);
598
599
v1 = theEmbedding->G[J].v;
600
v2 = theEmbedding->G[JTwin].v;
601
602
pos1 = context->G[v1].pos;
603
pos2 = context->G[v2].pos;
604
605
if (pos1 < pos2)
606
{
607
context->G[J].start = pos1;
608
context->G[J].end = pos2;
609
}
610
else
611
{
612
context->G[J].start = pos2;
613
context->G[J].end = pos1;
614
}
615
616
context->G[JTwin].start = context->G[J].start;
617
context->G[JTwin].end = context->G[J].end;
618
}
619
620
return OK;
621
}
622
623
/********************************************************************
624
_GetNextExternalFaceVertex()
625
Uses the extFace links to traverse to the next vertex on the external
626
face given a current vertex and the link that points to its predecessor.
627
********************************************************************/
628
int _GetNextExternalFaceVertex(graphP theGraph, int curVertex, int *pPrevLink)
629
{
630
int nextVertex, nextPrevLink;
631
632
nextVertex = theGraph->extFace[curVertex].vertex[1 ^ *pPrevLink];
633
634
// If the two links in the new vertex are not equal, then only one points
635
// back to the current vertex, and it is the new prev link.
636
if (theGraph->extFace[nextVertex].vertex[0] != theGraph->extFace[nextVertex].vertex[1])
637
{
638
nextPrevLink = theGraph->extFace[nextVertex].vertex[0]==curVertex ? 0 : 1;
639
}
640
else
641
{
642
int inverted = 0;
643
644
// One of the two vertices is the root of the bicomp. The non-root may
645
// not have the same orientation as the root because a consistent orientation
646
// is not imposed until post-processing of the planarity test. But the
647
// planarity test does special-case tracking of orientation inversions of
648
// singleton bicomps in the inversionFlag of the non-root vertex.
649
650
if (nextVertex < theGraph->N)
651
inverted = theGraph->extFace[nextVertex].inversionFlag;
652
else inverted = theGraph->extFace[curVertex].inversionFlag;
653
654
// If the curVertex and nextVertex are in a singleton and have the same
655
// orientation, then the prev link from the current vertex is the prev
656
// link from the next vertex because you'd just be going around in a
657
// small circle using the same links for prev every time.
658
// If their orientations are inverted, then we just invert the prev link.
659
660
nextPrevLink = inverted ? (1^*pPrevLink) : (*pPrevLink);
661
}
662
663
// Return the information
664
*pPrevLink = nextPrevLink;
665
return nextVertex;
666
}
667
668
/********************************************************************
669
_CollectDrawingData()
670
To be called by core planarity Walkdown immediately before merging
671
bicomps and embedding a new back edge.
672
673
Each bicomp is rooted by a DFS tree edge. The parent vertex in
674
that edge is the bicomp root, and the bicomp contains one DFS child
675
of the vertex, which is on the child end of the 'root edge'.
676
677
Here we decide whether the DFS child is to be embedded between or
678
beyond its parent relative to vertex I, the one currently being
679
processed (and the ancestor endpoint of a back edge being embedded,
680
where the descendant endpoint is also an endpoint of the bicomp
681
root being merged).
682
********************************************************************/
683
684
void _CollectDrawingData(DrawPlanarContext *context, int RootVertex, int W, int WPrevLink)
685
{
686
graphP theEmbedding = context->theGraph;
687
//int ancestorChild = RootVertex - theEmbedding->N;
688
//int ancestor = theEmbedding->V[ancestorChild].DFSParent;
689
int K, Parent, BicompRoot, DFSChild, direction, descendant;
690
691
gp_LogLine("\ngraphDrawPlanar.c/_CollectDrawingData() start");
692
gp_LogLine(gp_MakeLogStr3("_CollectDrawingData(RootVertex=%d, W=%d, W_in=%d)",
693
RootVertex, W, WPrevLink));
694
695
/* Process all of the merge points to set their drawing flags. */
696
697
for (K = 0; K < sp_GetCurrentSize(theEmbedding->theStack); K += 4)
698
{
699
/* Get the parent and child that are about to be merged from
700
the 4-tuple in the merge stack */
701
Parent = theEmbedding->theStack->S[K];
702
BicompRoot = theEmbedding->theStack->S[K+2];
703
DFSChild = BicompRoot - theEmbedding->N;
704
705
/* We get the active descendant vertex in the child bicomp that
706
will be adjacent to the parent along the external face.
707
This vertex is guaranteed to be found in one step
708
due to external face 'short-circuiting' that was done in
709
step 'Parent' of the planarity algorithm.
710
We pass theEmbedding->N for the second parameter because
711
of this; we use this function to signify need of extFace
712
links in the other implementation.*/
713
714
direction = theEmbedding->theStack->S[K+3];
715
descendant = _GetNextExternalFaceVertex(theEmbedding, BicompRoot, &direction);
716
717
/* Now we set the tie flag in the DFS child, and mark the
718
descendant and parent with non-NIL pointers to the child
719
whose tie flag is to be resolved as soon as one of the
720
two is connected to by an edge or child bicomp merge. */
721
722
context->V[DFSChild].drawingFlag = DRAWINGFLAG_TIE;
723
724
context->V[descendant].tie[direction] = DFSChild;
725
726
direction = theEmbedding->theStack->S[K+1];
727
context->V[Parent].tie[direction] = DFSChild;
728
729
gp_LogLine(gp_MakeLogStr5("V[Parent=%d]=.tie[%d] = V[descendant=%d].tie[%d] = (child=%d)",
730
Parent, direction, descendant, theEmbedding->theStack->S[K+3], DFSChild));
731
}
732
733
gp_LogLine("graphDrawPlanar.c/_CollectDrawingData() end\n");
734
}
735
736
/********************************************************************
737
_BreakTie()
738
739
The given vertex W has just been arrived at by the core planarity
740
algorithm. Using WPrevLink, we seek its predecessor WPred on the
741
external face and test whether the two are involved in a tie that
742
can be resolved.
743
744
Since the planarity algorithm has just passed by WPred, it is
745
safe to conclude that WPred can go between W and the current vertex.
746
747
Of course, if W was the parent to some DFS child whose subtree
748
contains WPred, then the DFS child is marked 'between', placing
749
the whole subtree including WPred between W and the current vertex.
750
On the other hand, if WPred was the parent of some DFS child whose
751
subtree contained W, then we achieve the same effect of putting WPred
752
'between' W and the curent vertex by marking the DFS child 'beyond'.
753
Since the DFS child and hence W are beyond W relative to the current
754
vertex, WPred is also between W and the current vertex.
755
756
Thus the certain positional relationship between W and WPred
757
relative to a specific ancestor, the current vertex, is used to
758
indirectly break the positional tie between MIN(W, WPred) and the
759
DFS child of MIN(W, WPred) whose subtree contains MAX(W, WPred).
760
761
The ancestorChild is the DFS child of the current vertex whose DFS
762
subtree contains W and WPred, and it is recorded here in order to
763
optimize the post-processing calculation of vertex positions.
764
********************************************************************/
765
766
int _BreakTie(DrawPlanarContext *context, int BicompRoot, int W, int WPrevLink)
767
{
768
graphP theEmbedding = context->theGraph;
769
770
/* First we get the predecessor of W. */
771
772
int WPredNextLink = 1^WPrevLink,
773
WPred = _GetNextExternalFaceVertex(theEmbedding, W, &WPredNextLink);
774
775
gp_LogLine("\ngraphDrawPlanar.c/::_BreakTie() start");
776
gp_LogLine(gp_MakeLogStr3("_BreakTie(BicompRoot=%d, W=%d, W_in=%d)",
777
BicompRoot, W, WPrevLink));
778
779
/* Ties happen only within a bicomp (i.e. between two non-root vertices) */
780
if (W > theEmbedding->N || WPred >= theEmbedding->N)
781
{
782
gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n");
783
return OK;
784
}
785
786
/* The two vertices are either tied or not; having one tied and the other
787
not is an error */
788
789
if (context->V[W].tie[WPrevLink] != context->V[WPred].tie[WPredNextLink])
790
return NOTOK;
791
792
/* If there is a tie, it can now be resolved. */
793
if (context->V[W].tie[WPrevLink] != NIL)
794
{
795
int DFSChild = context->V[W].tie[WPrevLink];
796
797
/* Set the two ancestor variables that contextualize putting W 'between'
798
or 'beyond' its parent relative to what. */
799
800
context->V[DFSChild].ancestorChild = BicompRoot - theEmbedding->N;
801
context->V[DFSChild].ancestor = theEmbedding->V[BicompRoot - theEmbedding->N].DFSParent;
802
803
gp_LogLine(gp_MakeLogStr4("V[child=%d]=.ancestorChild = %d, V[child=%d]=.ancestor = %d",
804
DFSChild, context->V[DFSChild].ancestorChild, DFSChild, context->V[DFSChild].ancestor));
805
806
/* If W is the ancestor of WPred, then the DFSChild subtree contains
807
WPred, and so must go between W and some ancestor. */
808
if (W < WPred)
809
{
810
context->V[DFSChild].drawingFlag = DRAWINGFLAG_BETWEEN;
811
gp_LogLine(gp_MakeLogStr3("Child=%d is 'between' ancestorChild=%d and ancestor=%d",
812
DFSChild, context->V[DFSChild].ancestorChild, context->V[DFSChild].ancestor));
813
}
814
815
/* If W is the descendant, so we achieve the effect of putting WPred
816
between DFSChild and ancestor by putting the DFSChild 'beyond' WPred. */
817
else
818
{
819
context->V[DFSChild].drawingFlag = DRAWINGFLAG_BEYOND;
820
gp_LogLine(gp_MakeLogStr3("Child=%d is 'beyond' ancestorChild=%d relative to ancestor=%d",
821
DFSChild, context->V[DFSChild].ancestorChild, context->V[DFSChild].ancestor));
822
}
823
824
/* The tie is resolved so clear the flags*/
825
context->V[W].tie[WPrevLink] = context->V[WPred].tie[WPredNextLink] = NIL;
826
}
827
else
828
return NOTOK;
829
830
gp_LogLine("graphDrawPlanar.c/_BreakTie() end\n");
831
return OK;
832
}
833
834
/********************************************************************
835
_RenderToString()
836
Draws the previously calculated visibility representation in a
837
string of size (M+1)*2N + 1 characters, which should be deallocated
838
with free().
839
840
Returns NULL on failure, or the string containing the visibility
841
representation otherwise. The string can be printed using %s,
842
********************************************************************/
843
844
char *_RenderToString(graphP theEmbedding)
845
{
846
DrawPlanarContext *context = NULL;
847
gp_FindExtension(theEmbedding, DRAWPLANAR_ID, (void *) &context);
848
849
if (context != NULL)
850
{
851
int N = theEmbedding->N;
852
int M = theEmbedding->M;
853
int I, J, e, Mid, Pos;
854
char *visRep = (char *) malloc(sizeof(char) * ((M+1) * 2*N + 1));
855
char numBuffer[32];
856
857
if (visRep == NULL)
858
return NULL;
859
860
if (sp_NonEmpty(context->theGraph->edgeHoles))
861
{
862
free(visRep);
863
return NULL;
864
}
865
866
// Clear the space
867
for (I = 0; I < N; I++)
868
{
869
for (J=0; J < M; J++)
870
{
871
visRep[(2*I) * (M+1) + J] = ' ';
872
visRep[(2*I+1) * (M+1) + J] = ' ';
873
}
874
875
visRep[(2*I) * (M+1) + M] = '\n';
876
visRep[(2*I+1) * (M+1) + M] = '\n';
877
}
878
879
// Draw the vertices
880
for (I = 0; I < N; I++)
881
{
882
Pos = context->G[I].pos;
883
for (J=context->G[I].start; J<=context->G[I].end; J++)
884
visRep[(2*Pos) * (M+1) + J] = '-';
885
886
// Draw vertex label
887
Mid = (context->G[I].start + context->G[I].end)/2;
888
sprintf(numBuffer, "%d", I);
889
if ((unsigned)(context->G[I].end - context->G[I].start + 1) >= strlen(numBuffer))
890
{
891
strncpy(visRep + (2*Pos) * (M+1) + Mid, numBuffer, strlen(numBuffer));
892
}
893
// If the vertex width is less than the label width, then fail gracefully
894
else
895
{
896
if (strlen(numBuffer)==2)
897
visRep[(2*Pos) * (M+1) + Mid] = numBuffer[0];
898
else
899
visRep[(2*Pos) * (M+1) + Mid] = '*';
900
901
visRep[(2*Pos+1) * (M+1) + Mid] = numBuffer[strlen(numBuffer)-1];
902
}
903
}
904
905
// Draw the edges
906
for (e=0; e<M; e++)
907
{
908
J = 2*N + 2*e;
909
Pos = context->G[J].pos;
910
for (I=context->G[J].start; I<context->G[J].end; I++)
911
{
912
if (I > context->G[J].start)
913
visRep[(2*I) * (M+1) + Pos] = '|';
914
visRep[(2*I+1) * (M+1) + Pos] = '|';
915
}
916
}
917
918
// Null terminate string and return it
919
visRep[(M+1) * 2*N] = '\0';
920
return visRep;
921
}
922
923
return NULL;
924
}
925
926
/********************************************************************
927
gp_DrawPlanar_RenderToFile()
928
Creates a rendition of the planar graph visibility representation
929
as a string, then dumps the string to the file.
930
********************************************************************/
931
int gp_DrawPlanar_RenderToFile(graphP theEmbedding, char *theFileName)
932
{
933
if (sp_IsEmpty(theEmbedding->edgeHoles))
934
{
935
FILE *outfile;
936
char *theRendition;
937
938
if (strcmp(theFileName, "stdout") == 0)
939
outfile = stdout;
940
else if (strcmp(theFileName, "stderr") == 0)
941
outfile = stderr;
942
else outfile = fopen(theFileName, WRITETEXT);
943
944
if (outfile == NULL)
945
return NOTOK;
946
947
theRendition = _RenderToString(theEmbedding);
948
if (theRendition != NULL)
949
{
950
fprintf(outfile, "%s", theRendition);
951
free(theRendition);
952
}
953
954
if (strcmp(theFileName, "stdout") == 0 || strcmp(theFileName, "stderr") == 0)
955
fflush(outfile);
956
957
else if (fclose(outfile) != 0)
958
return NOTOK;
959
960
return theRendition ? OK : NOTOK;
961
}
962
963
return NOTOK;
964
}
965
966
/********************************************************************
967
_CheckVisibilityRepresentationIntegrity()
968
********************************************************************/
969
970
int _CheckVisibilityRepresentationIntegrity(DrawPlanarContext *context)
971
{
972
graphP theEmbedding = context->theGraph;
973
int I, e, J, JTwin, JPos, JIndex;
974
975
if (sp_NonEmpty(context->theGraph->edgeHoles))
976
return NOTOK;
977
978
_FillVisitedFlags(theEmbedding, 0);
979
980
/* Test whether the vertex values make sense and
981
whether the vertex positions are unique. */
982
983
for (I = 0; I < theEmbedding->N; I++)
984
{
985
if (theEmbedding->M > 0)
986
{
987
if (context->G[I].pos < 0 ||
988
context->G[I].pos >= theEmbedding->N ||
989
context->G[I].start < 0 ||
990
context->G[I].start > context->G[I].end ||
991
context->G[I].end >= theEmbedding->M)
992
return NOTOK;
993
}
994
995
// Has the vertex position (context->G[I].pos) been used by a
996
// vertex before vertex I?
997
if (theEmbedding->G[context->G[I].pos].visited)
998
return NOTOK;
999
1000
// Mark the vertex position as used by vertex I.
1001
// Note that this marking is made on some other vertex unrelated to I
1002
// We're just reusing the vertex visited array as cheap storage for a
1003
// detector of reusing vertex position integers.
1004
theEmbedding->G[context->G[I].pos].visited = 1;
1005
}
1006
1007
/* Test whether the edge values make sense and
1008
whether the edge positions are unique */
1009
1010
for (e = 0; e < theEmbedding->M; e++)
1011
{
1012
/* Each edge has an index location J in the graph structure */
1013
J = theEmbedding->edgeOffset + 2*e;
1014
JTwin = gp_GetTwinArc(theEmbedding, J);
1015
1016
if (context->G[J].pos != context->G[JTwin].pos ||
1017
context->G[J].start != context->G[JTwin].start ||
1018
context->G[J].end != context->G[JTwin].end ||
1019
context->G[J].pos < 0 ||
1020
context->G[J].pos >= theEmbedding->M ||
1021
context->G[J].start < 0 ||
1022
context->G[J].start > context->G[J].end ||
1023
context->G[J].end >= theEmbedding->N)
1024
return NOTOK;
1025
1026
/* Get the recorded horizontal position of that edge,
1027
a number between 0 and M-1 */
1028
1029
JPos = context->G[J].pos;
1030
1031
/* Convert that to an index in the graph structure so we
1032
can use the visited flags in the graph's edges to
1033
tell us whether the positions are being reused. */
1034
1035
JIndex = theEmbedding->edgeOffset + 2*JPos;
1036
JTwin = gp_GetTwinArc(theEmbedding, JIndex);
1037
1038
if (theEmbedding->G[JIndex].visited || theEmbedding->G[JTwin].visited)
1039
return NOTOK;
1040
1041
theEmbedding->G[JIndex].visited = theEmbedding->G[JTwin].visited = 1;
1042
}
1043
1044
/* Test whether any edge intersects any vertex position
1045
for a vertex that is not an endpoint of the edge. */
1046
1047
for (e = 0; e < theEmbedding->M; e++)
1048
{
1049
J = theEmbedding->edgeOffset + 2*e;
1050
JTwin = gp_GetTwinArc(theEmbedding, J);
1051
1052
for (I = 0; I < theEmbedding->N; I++)
1053
{
1054
/* If the vertex is an endpoint of the edge, then... */
1055
1056
if (theEmbedding->G[J].v == I || theEmbedding->G[JTwin].v == I)
1057
{
1058
/* The vertical position of the vertex must be
1059
at the top or bottom of the edge, */
1060
if (context->G[J].start != context->G[I].pos &&
1061
context->G[J].end != context->G[I].pos)
1062
return NOTOK;
1063
1064
/* The horizontal edge position must be in the range of the vertex */
1065
if (context->G[J].pos < context->G[I].start ||
1066
context->G[J].pos > context->G[I].end)
1067
return NOTOK;
1068
}
1069
1070
/* If the vertex is not an endpoint of the edge... */
1071
1072
else // if (theEmbedding->G[J].v != I && theEmbedding->G[JTwin].v != I)
1073
{
1074
/* If the vertical position of the vertex is in the
1075
vertical range of the edge ... */
1076
1077
if (context->G[J].start <= context->G[I].pos &&
1078
context->G[J].end >= context->G[I].pos)
1079
{
1080
/* And if the horizontal position of the edge is in the
1081
horizontal range of the vertex, then return an error. */
1082
1083
if (context->G[I].start <= context->G[J].pos &&
1084
context->G[I].end >= context->G[J].pos)
1085
return NOTOK;
1086
}
1087
}
1088
}
1089
}
1090
1091
1092
/* All tests passed */
1093
1094
return OK;
1095
}
1096
1097