00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "neato.h"
00023 #include "agxbuf.h"
00024 #include "utils.h"
00025 #include "voronoi.h"
00026 #include "info.h"
00027 #include "edges.h"
00028 #include "site.h"
00029 #include "heap.h"
00030 #include "hedges.h"
00031 #include "digcola.h"
00032 #ifdef IPSEPCOLA
00033 #include <csolve_VPSC.h>
00034 #include "quad_prog_vpsc.h"
00035 #endif
00036
00037 static double margin = 0.05;
00038
00039
00040
00041 static double incr = 0.05;
00042
00043
00044 static double pmargin = 5.0 / POINTS_PER_INCH;
00045 static int iterations = -1;
00046 static int useIter = 0;
00047
00048 static int doAll = 0;
00049 static Site **sites;
00050 static Site **endSite;
00051 static Point nw, ne, sw, se;
00052
00053 static Site **nextSite;
00054
00055 static void setBoundBox(Point * ll, Point * ur)
00056 {
00057 pxmin = ll->x;
00058 pxmax = ur->x;
00059 pymin = ll->y;
00060 pymax = ur->y;
00061 nw.x = sw.x = pxmin;
00062 ne.x = se.x = pxmax;
00063 nw.y = ne.y = pymax;
00064 sw.y = se.y = pymin;
00065 }
00066
00067
00068
00069
00070 static void freeNodes(void)
00071 {
00072 int i;
00073 Info_t *ip = nodeInfo;
00074
00075 for (i = 0; i < nsites; i++) {
00076 breakPoly(&ip->poly);
00077 ip++;
00078 }
00079 polyFree();
00080 infoinit();
00081 free(nodeInfo);
00082 }
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092 static void chkBoundBox(Agraph_t * graph)
00093 {
00094 char *marg;
00095 Point ll, ur;
00096 int i;
00097 double x, y;
00098 double xmin, xmax, ymin, ymax;
00099 double xmn, xmx, ymn, ymx;
00100 double ydelta, xdelta;
00101 Info_t *ip;
00102 Poly *pp;
00103
00104
00105 ip = nodeInfo;
00106 pp = &ip->poly;
00107 x = ip->site.coord.x;
00108 y = ip->site.coord.y;
00109 xmin = pp->origin.x + x;
00110 ymin = pp->origin.y + y;
00111 xmax = pp->corner.x + x;
00112 ymax = pp->corner.y + y;
00113 for (i = 1; i < nsites; i++) {
00114 ip++;
00115 pp = &ip->poly;
00116 x = ip->site.coord.x;
00117 y = ip->site.coord.y;
00118 xmn = pp->origin.x + x;
00119 ymn = pp->origin.y + y;
00120 xmx = pp->corner.x + x;
00121 ymx = pp->corner.y + y;
00122 if (xmn < xmin)
00123 xmin = xmn;
00124 if (ymn < ymin)
00125 ymin = ymn;
00126 if (xmx > xmax)
00127 xmax = xmx;
00128 if (ymx > ymax)
00129 ymax = ymx;
00130 }
00131
00132 marg = agget(graph, "voro_margin");
00133 if (marg && (*marg != '\0')) {
00134 margin = atof(marg);
00135 }
00136 ydelta = margin * (ymax - ymin);
00137 xdelta = margin * (xmax - xmin);
00138 ll.x = xmin - xdelta;
00139 ll.y = ymin - ydelta;
00140 ur.x = xmax + xdelta;
00141 ur.y = ymax + ydelta;
00142
00143 setBoundBox(&ll, &ur);
00144 }
00145
00146
00147
00148
00149 static void makeInfo(Agraph_t * graph)
00150 {
00151 Agnode_t *node;
00152 int i;
00153 Info_t *ip;
00154
00155 nsites = agnnodes(graph);
00156 geominit();
00157
00158 nodeInfo = N_GNEW(nsites, Info_t);
00159
00160 node = agfstnode(graph);
00161 ip = nodeInfo;
00162
00163 pmargin = expFactor (graph);
00164 for (i = 0; i < nsites; i++) {
00165 ip->site.coord.x = ND_pos(node)[0];
00166 ip->site.coord.y = ND_pos(node)[1];
00167
00168 makePoly(&ip->poly, node, pmargin);
00169
00170 ip->site.sitenbr = i;
00171 ip->site.refcnt = 1;
00172 ip->node = node;
00173 ip->verts = NULL;
00174 node = agnxtnode(graph, node);
00175 ip++;
00176 }
00177 }
00178
00179
00180 static int scomp(const void *S1, const void *S2)
00181 {
00182 Site *s1, *s2;
00183
00184 s1 = *(Site **) S1;
00185 s2 = *(Site **) S2;
00186 if (s1->coord.y < s2->coord.y)
00187 return (-1);
00188 if (s1->coord.y > s2->coord.y)
00189 return (1);
00190 if (s1->coord.x < s2->coord.x)
00191 return (-1);
00192 if (s1->coord.x > s2->coord.x)
00193 return (1);
00194 return (0);
00195 }
00196
00197
00198
00199
00200 static void sortSites(void)
00201 {
00202 int i;
00203 Site **sp;
00204 Info_t *ip;
00205
00206 if (sites == 0) {
00207 sites = N_GNEW(nsites, Site *);
00208 endSite = sites + nsites;
00209 }
00210
00211 sp = sites;
00212 ip = nodeInfo;
00213 infoinit();
00214 for (i = 0; i < nsites; i++) {
00215 *sp++ = &(ip->site);
00216 ip->verts = NULL;
00217 ip->site.refcnt = 1;
00218 ip++;
00219 }
00220
00221 qsort(sites, nsites, sizeof(Site *), scomp);
00222
00223
00224 nextSite = sites;
00225
00226 }
00227
00228 static void geomUpdate(int doSort)
00229 {
00230 int i;
00231
00232 if (doSort)
00233 sortSites();
00234
00235
00236 xmin = sites[0]->coord.x;
00237 xmax = sites[0]->coord.x;
00238 for (i = 1; i < nsites; i++) {
00239 if (sites[i]->coord.x < xmin)
00240 xmin = sites[i]->coord.x;
00241 if (sites[i]->coord.x > xmax)
00242 xmax = sites[i]->coord.x;
00243 }
00244 ymin = sites[0]->coord.y;
00245 ymax = sites[nsites - 1]->coord.y;
00246
00247 deltay = ymax - ymin;
00248 deltax = xmax - xmin;
00249 }
00250
00251 static Site *nextOne(void)
00252 {
00253 Site *s;
00254
00255 if (nextSite < endSite) {
00256 s = *nextSite++;
00257 return (s);
00258 } else
00259 return ((Site *) NULL);
00260 }
00261
00262
00263
00264
00265
00266 static void rmEquality(void)
00267 {
00268 int i, cnt;
00269 Site **ip;
00270 Site **jp;
00271 Site **kp;
00272 double xdel;
00273
00274 sortSites();
00275 ip = sites;
00276
00277 while (ip < endSite) {
00278 jp = ip + 1;
00279 if ((jp >= endSite) ||
00280 ((*jp)->coord.x != (*ip)->coord.x) ||
00281 ((*jp)->coord.y != (*ip)->coord.y)) {
00282 ip = jp;
00283 continue;
00284 }
00285
00286
00287 cnt = 2;
00288 kp = jp + 1;
00289 while ((kp < endSite) &&
00290 ((*kp)->coord.x == (*ip)->coord.x) &&
00291 ((*kp)->coord.y == (*ip)->coord.y)) {
00292 cnt++;
00293 jp = kp;
00294 kp = jp + 1;
00295 }
00296
00297
00298 if ((kp < endSite) && ((*kp)->coord.y == (*ip)->coord.y)) {
00299 xdel = ((*kp)->coord.x - (*ip)->coord.x) / cnt;
00300 i = 1;
00301 for (jp = ip + 1; jp < kp; jp++) {
00302 (*jp)->coord.x += i * xdel;
00303 i++;
00304 }
00305 } else {
00306 Info_t *info;
00307 for (jp = ip + 1; jp < kp; ip++, jp++) {
00308 info = nodeInfo + (*ip)->sitenbr;
00309 xdel = info->poly.corner.x - info->poly.origin.x;
00310 info = nodeInfo + (*jp)->sitenbr;
00311 xdel += info->poly.corner.x - info->poly.origin.x;
00312 (*jp)->coord.x = (*ip)->coord.x + xdel / 2;
00313 }
00314 }
00315 ip = kp;
00316 }
00317 }
00318
00319
00320
00321
00322 static int countOverlap(int iter)
00323 {
00324 int count = 0;
00325 int i, j;
00326 Info_t *ip = nodeInfo;
00327 Info_t *jp;
00328
00329 for (i = 0; i < nsites; i++)
00330 nodeInfo[i].overlaps = 0;
00331
00332 for (i = 0; i < nsites - 1; i++) {
00333 jp = ip + 1;
00334 for (j = i + 1; j < nsites; j++) {
00335 if (polyOverlap
00336 (ip->site.coord, &ip->poly, jp->site.coord, &jp->poly)) {
00337 count++;
00338 ip->overlaps = 1;
00339 jp->overlaps = 1;
00340 }
00341 jp++;
00342 }
00343 ip++;
00344 }
00345
00346 if (Verbose > 1)
00347 fprintf(stderr, "overlap [%d] : %d\n", iter, count);
00348 return count;
00349 }
00350
00351 static void increaseBoundBox(void)
00352 {
00353 double ydelta, xdelta;
00354 Point ll, ur;
00355
00356 ur.x = pxmax;
00357 ur.y = pymax;
00358 ll.x = pxmin;
00359 ll.y = pymin;
00360
00361 ydelta = incr * (ur.y - ll.y);
00362 xdelta = incr * (ur.x - ll.x);
00363
00364 ur.x += xdelta;
00365 ur.y += ydelta;
00366 ll.x -= xdelta;
00367 ll.y -= ydelta;
00368
00369 setBoundBox(&ll, &ur);
00370 }
00371
00372
00373
00374
00375 static double areaOf(Point a, Point b, Point c)
00376 {
00377 double area;
00378
00379 area =
00380 (double) (fabs
00381 (a.x * (b.y - c.y) + b.x * (c.y - a.y) +
00382 c.x * (a.y - b.y)) / 2);
00383 return area;
00384 }
00385
00386
00387
00388
00389
00390 static void centroidOf(Point a, Point b, Point c, double *x, double *y)
00391 {
00392 *x = (a.x + b.x + c.x) / 3;
00393 *y = (a.y + b.y + c.y) / 3;
00394 }
00395
00396
00397
00398
00399
00400
00401
00402 static void newpos(Info_t * ip)
00403 {
00404 PtItem *anchor = ip->verts;
00405 PtItem *p, *q;
00406 double totalArea = 0.0;
00407 double cx = 0.0;
00408 double cy = 0.0;
00409 double x;
00410 double y;
00411 double area;
00412
00413 p = anchor->next;
00414 q = p->next;
00415 while (q != NULL) {
00416 area = areaOf(anchor->p, p->p, q->p);
00417 centroidOf(anchor->p, p->p, q->p, &x, &y);
00418 cx = cx + area * x;
00419 cy = cy + area * y;
00420 totalArea = totalArea + area;
00421 p = q;
00422 q = q->next;
00423 }
00424
00425 ip->site.coord.x = cx / totalArea;
00426 ip->site.coord.y = cy / totalArea;
00427 }
00428
00429
00430
00431
00432
00433 static void addCorners(void)
00434 {
00435 Info_t *ip = nodeInfo;
00436 Info_t *sws = ip;
00437 Info_t *nws = ip;
00438 Info_t *ses = ip;
00439 Info_t *nes = ip;
00440 double swd = dist_2(&ip->site.coord, &sw);
00441 double nwd = dist_2(&ip->site.coord, &nw);
00442 double sed = dist_2(&ip->site.coord, &se);
00443 double ned = dist_2(&ip->site.coord, &ne);
00444 double d;
00445 int i;
00446
00447 ip++;
00448 for (i = 1; i < nsites; i++) {
00449 d = dist_2(&ip->site.coord, &sw);
00450 if (d < swd) {
00451 swd = d;
00452 sws = ip;
00453 }
00454 d = dist_2(&ip->site.coord, &se);
00455 if (d < sed) {
00456 sed = d;
00457 ses = ip;
00458 }
00459 d = dist_2(&ip->site.coord, &nw);
00460 if (d < nwd) {
00461 nwd = d;
00462 nws = ip;
00463 }
00464 d = dist_2(&ip->site.coord, &ne);
00465 if (d < ned) {
00466 ned = d;
00467 nes = ip;
00468 }
00469 ip++;
00470 }
00471
00472 addVertex(&sws->site, sw.x, sw.y);
00473 addVertex(&ses->site, se.x, se.y);
00474 addVertex(&nws->site, nw.x, nw.y);
00475 addVertex(&nes->site, ne.x, ne.y);
00476 }
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486 static void newPos(void)
00487 {
00488 int i;
00489 Info_t *ip = nodeInfo;
00490
00491 addCorners();
00492 for (i = 0; i < nsites; i++) {
00493 if (doAll || ip->overlaps)
00494 newpos(ip);
00495 ip++;
00496 }
00497 }
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507 static void cleanup(void)
00508 {
00509 PQcleanup();
00510 ELcleanup();
00511 siteinit();
00512 edgeinit();
00513 }
00514
00515 static int vAdjust(void)
00516 {
00517 int iterCnt = 0;
00518 int overlapCnt = 0;
00519 int badLevel = 0;
00520 int increaseCnt = 0;
00521 int cnt;
00522
00523 if (!useIter || (iterations > 0))
00524 overlapCnt = countOverlap(iterCnt);
00525
00526 if ((overlapCnt == 0) || (iterations == 0))
00527 return 0;
00528
00529 rmEquality();
00530 geomUpdate(0);
00531 voronoi(0, nextOne);
00532 while (1) {
00533 newPos();
00534 iterCnt++;
00535
00536 if (useIter && (iterCnt == iterations))
00537 break;
00538 cnt = countOverlap(iterCnt);
00539 if (cnt == 0)
00540 break;
00541 if (cnt >= overlapCnt)
00542 badLevel++;
00543 else
00544 badLevel = 0;
00545 overlapCnt = cnt;
00546
00547 switch (badLevel) {
00548 case 0:
00549 doAll = 1;
00550 break;
00551
00552
00553
00554
00555
00556 default:
00557 doAll = 1;
00558 increaseCnt++;
00559 increaseBoundBox();
00560 break;
00561 }
00562
00563 geomUpdate(1);
00564 voronoi(0, nextOne);
00565 }
00566
00567 if (Verbose) {
00568 fprintf(stderr, "Number of iterations = %d\n", iterCnt);
00569 fprintf(stderr, "Number of increases = %d\n", increaseCnt);
00570 }
00571
00572 cleanup();
00573 return 1;
00574 }
00575
00576 static double rePos(Point c)
00577 {
00578 int i;
00579 Info_t *ip = nodeInfo;
00580 double f = 1.0 + incr;
00581
00582 for (i = 0; i < nsites; i++) {
00583
00584
00585 ip->site.coord.x = f * ip->site.coord.x;
00586 ip->site.coord.y = f * ip->site.coord.y;
00587 ip++;
00588 }
00589 return f;
00590 }
00591
00592 static int sAdjust(void)
00593 {
00594 int iterCnt = 0;
00595 int overlapCnt = 0;
00596 int cnt;
00597 Point center;
00598
00599
00600 if (!useIter || (iterations > 0))
00601 overlapCnt = countOverlap(iterCnt);
00602
00603 if ((overlapCnt == 0) || (iterations == 0))
00604 return 0;
00605
00606 rmEquality();
00607 center.x = (pxmin + pxmax) / 2.0;
00608 center.y = (pymin + pymax) / 2.0;
00609 while (1) {
00610 rePos(center);
00611 iterCnt++;
00612
00613 if (useIter && (iterCnt == iterations))
00614 break;
00615 cnt = countOverlap(iterCnt);
00616 if (cnt == 0)
00617 break;
00618 }
00619
00620 if (Verbose) {
00621 fprintf(stderr, "Number of iterations = %d\n", iterCnt);
00622 }
00623
00624 return 1;
00625 }
00626
00627
00628
00629
00630 static void updateGraph(Agraph_t * graph)
00631 {
00632
00633 int i;
00634 Info_t *ip;
00635
00636
00637 ip = nodeInfo;
00638 for (i = 0; i < nsites; i++) {
00639 ND_pos(ip->node)[0] = ip->site.coord.x;
00640 ND_pos(ip->node)[1] = ip->site.coord.y;
00641 ip++;
00642 }
00643 }
00644
00645 #ifdef IPSEPCOLA
00646 static int
00647 vpscAdjust(graph_t* G)
00648 {
00649 int dim = 2;
00650 int nnodes = agnnodes(G);
00651 ipsep_options opt;
00652 pointf* nsize = N_GNEW(nnodes, pointf);
00653 float** coords = N_GNEW(dim, float*);
00654 float* f_storage = N_GNEW(dim * nnodes, float);
00655 int i, j;
00656 Agnode_t* v;
00657 char* str;
00658
00659 for (i = 0; i < dim; i++) {
00660 coords[i] = f_storage + i * nnodes;
00661 }
00662
00663 j = 0;
00664 for (v = agfstnode(G); v; v = agnxtnode(G, v)) {
00665 for (i = 0; i < dim; i++) {
00666 coords[i][j] = (float) (ND_pos(v)[i]);
00667 }
00668 nsize[j].x = ND_width(v);
00669 nsize[j].y = ND_height(v);
00670 j++;
00671 }
00672
00673 opt.diredges = 0;
00674 opt.edge_gap = 0;
00675 opt.noverlap = 2;
00676 opt.clusters = NEW(cluster_data);
00677 if ((str = agget(G, "sep")) &&
00678 (i = sscanf(str, "%lf,%lf", &opt.gap.x, &opt.gap.y))) {
00679 if (i == 1) opt.gap.y = opt.gap.x;
00680 if(Verbose)
00681 fprintf(stderr,"gap=%f,%f\n",opt.gap.x,opt.gap.y);
00682 }
00683 else opt.gap.x = opt.gap.y = PS2INCH(10);
00684 opt.nsize = nsize;
00685
00686 removeoverlaps(nnodes, coords, &opt);
00687
00688 j = 0;
00689 for (v = agfstnode(G); v; v = agnxtnode(G, v)) {
00690 for (i = 0; i < dim; i++) {
00691 ND_pos(v)[i] = coords[i][j];
00692 }
00693 j++;
00694 }
00695
00696 free (opt.clusters);
00697 free (f_storage);
00698 free (coords);
00699 free (nsize);
00700 return 0;
00701 }
00702 #endif
00703
00704
00705
00706
00707
00708
00709 void normalize(graph_t * g)
00710 {
00711 node_t *v;
00712 edge_t *e;
00713
00714 double theta;
00715 pointf p;
00716
00717 if (!mapbool(agget(g, "normalize")))
00718 return;
00719
00720 v = agfstnode(g);
00721 p.x = ND_pos(v)[0];
00722 p.y = ND_pos(v)[1];
00723 for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00724 ND_pos(v)[0] -= p.x;
00725 ND_pos(v)[1] -= p.y;
00726 }
00727
00728 e = NULL;
00729 for (v = agfstnode(g); v; v = agnxtnode(g, v))
00730 if ((e = agfstout(g, v)))
00731 break;
00732 if (e == NULL)
00733 return;
00734
00735 theta = -atan2(ND_pos(e->head)[1] - ND_pos(e->tail)[1],
00736 ND_pos(e->head)[0] - ND_pos(e->tail)[0]);
00737
00738 for (v = agfstnode(g); v; v = agnxtnode(g, v)) {
00739 p.x = ND_pos(v)[0];
00740 p.y = ND_pos(v)[1];
00741 ND_pos(v)[0] = p.x * cos(theta) - p.y * sin(theta);
00742 ND_pos(v)[1] = p.x * sin(theta) + p.y * cos(theta);
00743 }
00744 }
00745
00746 static adjust_data adjustMode[] = {
00747 {AM_NONE, "", ""},
00748 {AM_VOR, "", "Voronoi"},
00749 {AM_SCALE, "oscale", "old scaling"},
00750 {AM_NSCALE, "scale", "scaling"},
00751 {AM_SCALEXY, "scalexy", "x and y scaling"},
00752
00753
00754 {AM_ORTHO, "ortho", "orthogonal constraints"},
00755 {AM_ORTHO_YX, "ortho_yx", "orthogonal constraints"},
00756 {AM_ORTHOXY, "orthoxy", "xy orthogonal constraints"},
00757 {AM_ORTHOYX, "orthoyx", "yx orthogonal constraints"},
00758 {AM_PORTHO, "portho", "pseudo-orthogonal constraints"},
00759 {AM_PORTHO_YX, "portho_yx", "pseudo-orthogonal constraints"},
00760 {AM_PORTHOXY, "porthoxy", "xy pseudo-orthogonal constraints"},
00761 {AM_PORTHOYX, "porthoyx", "yx pseudo-orthogonal constraints"},
00762 {AM_COMPRESS, "compress", "compress"},
00763 {AM_VPSC, "vpsc", "vpsc"},
00764 {AM_IPSEP, "ipsep", "ipsep"},
00765 {AM_NONE, 0, 0}
00766 };
00767
00768
00769
00770
00771
00772 static adjust_data *getAdjustMode(char *s)
00773 {
00774 adjust_data *ap = adjustMode + 2;
00775 if (*s == '\0') return adjustMode;
00776 while (ap->attrib) {
00777 if (!strcasecmp(s, ap->attrib))
00778 return ap;
00779 ap++;
00780 }
00781 if (mapbool(s))
00782 return adjustMode;
00783 else
00784 return adjustMode + 1;
00785 }
00786
00787 adjust_data *graphAdjustMode(graph_t *G)
00788 {
00789 char* am = agget(G, "overlap");
00790 return (getAdjustMode (am ? am : ""));
00791 }
00792
00793
00794
00795
00796
00797 int
00798 removeOverlapAs(graph_t * G, char* flag)
00799 {
00800
00801 int ret = 0;
00802
00803
00804 adjust_data *am;
00805
00806 if (agnnodes(G) < 2)
00807 return 0;
00808 if (flag == NULL)
00809 return 0;
00810
00811 am = getAdjustMode(flag);
00812 if (am->mode == AM_NONE)
00813 return 0;
00814
00815 if (Verbose)
00816 fprintf(stderr, "Adjusting %s using %s\n", G->name, am->print);
00817
00818 if (am->mode > AM_SCALE) {
00819
00820 switch (am->mode) {
00821 case AM_NSCALE:
00822 ret = scAdjust(G, 1);
00823 break;
00824 case AM_SCALEXY:
00825 ret = scAdjust(G, 0);
00826 break;
00827 case AM_PUSH:
00828
00829 break;
00830 case AM_PUSHPULL:
00831
00832 break;
00833 case AM_PORTHO_YX:
00834 case AM_PORTHO:
00835 case AM_PORTHOXY:
00836 case AM_PORTHOYX:
00837 case AM_ORTHO_YX:
00838 case AM_ORTHO:
00839 case AM_ORTHOXY:
00840 case AM_ORTHOYX:
00841 cAdjust(G, am->mode);
00842 break;
00843 case AM_COMPRESS:
00844 ret = scAdjust(G, -1);
00845 break;
00846 #ifdef IPSEPCOLA
00847 case AM_VPSC:
00848 ret = vpscAdjust(G);
00849 break;
00850 #endif
00851 default:
00852 break;
00853 }
00854
00855 return ret;
00856 }
00857
00858
00859
00860 makeInfo(G);
00861
00862
00863 chkBoundBox(G);
00864
00865 if (am->mode == AM_SCALE)
00866 ret = sAdjust();
00867 else
00868 ret = vAdjust();
00869
00870 if (ret)
00871 updateGraph(G);
00872
00873 freeNodes();
00874 free(sites);
00875 sites = 0;
00876
00877
00878 return ret;
00879 }
00880
00881
00882
00883 int
00884 removeOverlap(graph_t * G)
00885 {
00886 return (removeOverlapAs(G, agget(G, "overlap")));
00887 }
00888
00889
00890
00891
00892
00893 int adjustNodes(graph_t * G)
00894 {
00895 if (agnnodes(G) < 2)
00896 return 0;
00897 normalize(G);
00898 return removeOverlap (G);
00899 }
00900
00901
00902
00903
00904 double
00905 expFactor(graph_t* g)
00906 {
00907 double pmargin;
00908 char* marg;
00909
00910 if ((marg = agget(g, "sep")))
00911 pmargin = 1.0 + atof(marg);
00912 else if ((marg = agget(g, "esep")))
00913 pmargin = 1.0 + atof(marg)/SEPFACT;
00914 else
00915 pmargin = 1.1;
00916 return pmargin;
00917 }
00918