2
This software may only be used by you under license from AT&T Corp.
3
("AT&T"). A copy of AT&T's Source Code Agreement is available at
4
AT&T's Internet website having the URL:
5
<http://www.research.att.com/sw/tools/graphviz/license/source.html>
6
If you received this software without first entering into a license
7
with AT&T, you have an infringing copy of this software and cannot use
8
it without violating AT&T's intellectual property rights.
12
* Routines for repositioning nodes after initial layout in
13
* order to reduce/remove node overlaps.
27
static float margin = 0.05; /* Create initial bounding box by adding
28
* margin * dimension around box enclosing
31
static float incr = 0.05; /* Increase bounding box by adding
32
* incr * dimension around box.
34
static float pmargin = 5.0/72; /* Margin around polygons, in inches */
35
static int iterations = -1; /* Number of iterations */
36
static int useIter = 0; /* Use specified number of iterations */
38
static int doAll = 0; /* Move all nodes, regardless of overlap */
39
static Site** sites; /* Array of pointers to sites; used in qsort */
40
static Site** endSite; /* Sentinel on sites array */
41
static Point nw, ne, sw, se; /* Corners of clipping window */
43
static Site** nextSite;
46
setBoundBox (Point* ll, Point* ur)
59
* Free node resources.
65
Info_t* ip = nodeInfo;
67
for (i=0; i < nsites; i++) {
68
breakPoly (&ip->poly);
72
infoinit (); /* Free vertices */
77
* Compute extremes of graph, then set up bounding box.
78
* If user supplied a bounding box, use that;
79
* else if "window" is a graph attribute, use that;
80
* otherwise, define bounding box as a percentage expansion of
82
* In the first two cases, check that graph fits in bounding box.
85
chkBoundBox (Agraph_t* graph)
91
float xmin, xmax, ymin, ymax;
92
float xmn, xmx, ymn, ymx;
100
x = ip->site.coord.x;
101
y = ip->site.coord.y;
102
xmin = pp->origin.x + x;
103
ymin = pp->origin.y + y;
104
xmax = pp->corner.x + x;
105
ymax = pp->corner.y + y;
106
for(i = 1; i < nsites; i++) {
109
x = ip->site.coord.x;
110
y = ip->site.coord.y;
111
xmn = pp->origin.x + x;
112
ymn = pp->origin.y + y;
113
xmx = pp->corner.x + x;
114
ymx = pp->corner.y + y;
115
if(xmn < xmin) xmin = xmn;
116
if(ymn < ymin) ymin = ymn;
117
if(xmx > xmax) xmax = xmx;
118
if(ymx > ymax) ymax = ymx;
121
marg = agget (graph, "voro_margin");
122
if (marg && (*marg != '\0')) {
123
margin = atof (marg);
125
ydelta = margin * (ymax - ymin);
126
xdelta = margin * (xmax - xmin);
127
ll.x = xmin - xdelta;
128
ll.y = ymin - ydelta;
129
ur.x = xmax + xdelta;
130
ur.y = ymax + ydelta;
132
setBoundBox (&ll, &ur);
136
* For each node in the graph, create a Info data structure
139
makeInfo(Agraph_t* graph)
146
nsites = agnnodes (graph);
149
nodeInfo = (Info_t *) malloc(nsites * (sizeof (Info_t)));
151
node = agfstnode (graph);
155
marg = agget (graph, "voro_pmargin");
156
if (marg && (*marg != '\0')) {
157
pmargin = atof (marg);
160
if ((marg = agget(graph,"sep"))) {pmargin = 1.0 + atof(marg);}
164
for (i = 0; i < nsites; i++) {
165
ip->site.coord.x = node->u.pos[0];
166
ip->site.coord.y = node->u.pos[1];
168
makePoly (&ip->poly, node, pmargin);
170
ip->site.sitenbr = i;
174
node = agnxtnode (graph, node);
179
/* sort sites on y, then x, coord */
181
scomp(const void *S1, const void *S2)
187
if(s1 -> coord.y < s2 -> coord.y) return(-1);
188
if(s1 -> coord.y > s2 -> coord.y) return(1);
189
if(s1 -> coord.x < s2 -> coord.x) return(-1);
190
if(s1 -> coord.x > s2 -> coord.x) return(1);
195
* Fill array of pointer to sites and sort the sites using scomp
205
sites = (Site**)malloc(nsites * sizeof(Site*));
206
endSite = sites + nsites;
212
for (i=0; i < nsites; i++) {
219
qsort(sites, nsites, sizeof (Site *), scomp);
221
/* Reset site index for nextOne */
234
xmin=sites[0]->coord.x;
235
xmax=sites[0]->coord.x;
236
for(i = 1; i < nsites; i++) {
237
if(sites[i]->coord.x < xmin) xmin = sites[i]->coord.x;
238
if(sites[i]->coord.x > xmax) xmax = sites[i]->coord.x;
240
ymin = sites[0]->coord.y;
241
ymax = sites[nsites-1]->coord.y;
243
deltay = ymax - ymin;
244
deltax = xmax - xmin;
252
if(nextSite < endSite) {
257
return((Site *)NULL);
261
countOverlap (int iter)
265
Info_t* ip = nodeInfo;
268
for (i = 0; i < nsites; i++)
269
nodeInfo[i].overlaps = 0;
271
for (i = 0; i < nsites-1; i++) {
273
for (j = i+1; j < nsites; j++) {
274
if (polyOverlap (ip->site.coord, &ip->poly, jp->site.coord, &jp->poly)){
285
fprintf (stderr, "overlap [%d] : %d\n", iter, count);
292
float ydelta, xdelta;
300
ydelta = incr * (ur.y - ll.y);
301
xdelta = incr * (ur.x - ll.x);
308
setBoundBox (&ll, &ur);
312
* Area of triangle whose vertices are a,b,c
315
areaOf (Point a,Point b,Point c)
319
area = (float)(fabs(a.x*(b.y-c.y) + b.x*(c.y-a.y) + c.x*(a.y-b.y))/2);
324
* Compute centroid of triangle with vertices a, b, c.
325
* Return coordinates in x and y.
328
centroidOf (Point a,Point b,Point c, float *x, float *y)
330
*x = (a.x + b.x + c.x)/3;
331
*y = (a.y + b.y + c.y)/3;
335
* The new position is the centroid of the
336
* voronoi polygon. This is the weighted sum of the
337
* centroids of a triangulation, normalized to the
343
PtItem* anchor = ip->verts;
345
float totalArea = 0.0;
355
area = areaOf (anchor->p, p->p, q->p);
356
centroidOf (anchor->p, p->p, q->p, &x, &y);
359
totalArea = totalArea + area;
364
ip->site.coord.x = cx/totalArea;
365
ip->site.coord.y = cy/totalArea;
369
* Add corners of clipping window to appropriate sites.
370
* A site gets a corner if it is the closest site to that corner.
375
Info_t *ip = nodeInfo;
380
float swd = dist_2(&ip->site.coord, &sw);
381
float nwd = dist_2(&ip->site.coord, &nw);
382
float sed = dist_2(&ip->site.coord, &se);
383
float ned = dist_2(&ip->site.coord, &ne);
388
for (i = 1; i < nsites; i++) {
389
d = dist_2(&ip->site.coord, &sw);
394
d = dist_2(&ip->site.coord, &se);
399
d = dist_2(&ip->site.coord, &nw);
404
d = dist_2(&ip->site.coord, &ne);
412
addVertex (&sws->site, sw.x, sw.y);
413
addVertex (&ses->site, se.x, se.y);
414
addVertex (&nws->site, nw.x, nw.y);
415
addVertex (&nes->site, ne.x, ne.y);
419
* Calculate the new position of a site as the centroid
420
* of its voronoi polygon, if it overlaps other nodes.
421
* The polygons are finite by being clipped to the clipping
423
* We first add the corner of the clipping windows to the
424
* vertex lists of the appropriate sites.
430
Info_t* ip = nodeInfo;
433
for (i = 0; i < nsites; i++) {
434
if (doAll || ip->overlaps) newpos (ip);
448
if (!useIter || (iterations > 0))
449
overlapCnt = countOverlap (iterCnt);
451
if ((overlapCnt == 0) || (iterations == 0))
460
if (useIter && (iterCnt == iterations)) break;
461
cnt = countOverlap (iterCnt);
463
if (cnt >= overlapCnt) badLevel++;
488
fprintf (stderr, "Number of iterations = %d\n", iterCnt);
489
fprintf (stderr, "Number of increases = %d\n", increaseCnt);
499
Info_t* ip = nodeInfo;
500
double f = 1.0 + incr;
503
for (i = 0; i < nsites; i++) {
504
ip->site.coord.x = f*(ip->site.coord.x - c.x) + c.x;
505
ip->site.coord.y = f*(ip->site.coord.y - c.y) + c.y;
520
if (!useIter || (iterations > 0))
521
overlapCnt = countOverlap (iterCnt);
523
if ((overlapCnt == 0) || (iterations == 0))
526
center.x = (pxmin + pxmax)/2.0;
527
center.y = (pymin + pymax)/2.0;
532
if (useIter && (iterCnt == iterations)) break;
533
cnt = countOverlap (iterCnt);
538
fprintf (stderr, "Number of iterations = %d\n", iterCnt);
539
fprintf (stderr, "Number of increases = %d\n", increaseCnt);
546
* Enter new node positions into the graph
549
updateGraph (Agraph_t* graph)
551
/* Agnode_t* node; */
557
for (i = 0; i < nsites; i++) {
558
ip->node->u.pos[0] = ip->site.coord.x;
559
ip->node->u.pos[1] = ip->site.coord.y;
564
static void normalize(graph_t *g)
572
if (!mapbool(agget(g,"normalize"))) return;
574
v = agfstnode(g); p.x = v->u.pos[0]; p.y = v->u.pos[1];
575
for (v = agfstnode(g); v; v = agnxtnode(g,v))
576
{v->u.pos[0] -= p.x; v->u.pos[1] -= p.y;}
579
for (v = agfstnode(g); v; v = agnxtnode(g,v))
580
if ((e = agfstout(g,v))) break;
581
if (e == NULL) return;
583
theta = -atan2(e->head->u.pos[1] - e->tail->u.pos[1],
584
e->head->u.pos[0] - e->tail->u.pos[0]);
586
for (v = agfstnode(g); v; v = agnxtnode(g,v)) {
587
p.x = v->u.pos[0]; p.y = v->u.pos[1];
588
v->u.pos[0] = p.x * cos(theta) - p.y * sin(theta);
589
v->u.pos[1] = p.x * sin(theta) + p.y * cos(theta);
593
void adjustNodes (graph_t* G)
595
/* int userWindow = 0; */
601
flag = agget(G,"overlap");
602
if (flag == NULL) return;
603
if (!strcasecmp(flag,"scale")) doScale = 1;
604
else if (mapbool(flag)) return;
606
/* create main array */
609
/* establish and verify bounding box */
612
if (doScale) ret = sAdjust ();
613
else ret = vAdjust ();
615
if (ret) updateGraph (G);