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.
13
* every shape has these functions:
15
* void SHAPE_init(node_t *n)
16
* initialize the shape (usually at least its size).
17
* port_t SHAPE_port(node_t *n, char *portname)
18
* return the aiming point and slope (if constrained)
20
* int SHAPE_inside(node_t *n, pointf p, edge_t *e);
21
* test if point is inside the node shape which is
23
* the point is relative to the node center. the edge
24
* is passed in case the port affects spline clipping.
25
* void SHAPE_code(node_t *n)
26
* generate graphics code for a node.
27
* int SHAPE_path(node_t *n, edge_t *e, int pt, box path[], int *nbox)
28
* create a path for the port of e that touches n,
31
* some shapes, polygons in particular, use additional shape control data *
35
/* this needs an overhaul */
43
#define FILLED (1 << 0)
44
#define ROUNDED (1 << 1)
45
#define DIAGONALS (1 << 2)
46
#define AUXLABELS (1 << 3)
51
void sincos(x,s,c) double x,*s,*c; { *s = sin(x); *c = cos(x); }
53
extern void sincos(double x, double *s, double *c);
56
static port_t Center = { {0,0}, -1, 0, 0, 0};
61
{ int t = p.x; p.x = -p.y; p.y = t; return p; }
66
{ int t = p.x; p.x = p.y; p.y = -t; return p; }
70
{ double t = p.x; p.x = -p.y; p.y = t; return p; }
73
storeline(textlabel_t *lp, char *line, char terminator,graph_t *g)
77
lp->line = ALLOC(lp->nlines+2,lp->line,textline_t);
78
lp->line[lp->nlines].str = line;
80
if (CodeGen && CodeGen->textsize)
81
width = (CodeGen->textsize(line, lp->fontname,lp->fontsize).x);
83
width = estimate_textsize(line, lp->fontname,lp->fontsize).x;
85
lp->line[lp->nlines].width = width;
86
lp->line[lp->nlines].just = terminator;
89
width += lp->fontsize; /* margins */
90
width = PS2INCH(width);
91
if (lp->dimen.x < width) lp->dimen.x = width;
93
/* recalculate total height, including a small extra margin
94
* for top and bottom */
95
lp->dimen.y = PS2INCH(lp->nlines*(int)(lp->fontsize*LINESPACING) + 4);
98
/* compiles <str> into a label <lp> and returns its bounding box size. */
99
pointf label_size(char *str, textlabel_t *lp, graph_t *g)
101
char c,*p,*line,*lineptr;
103
line = lineptr = NULL;
105
line = lineptr = (char*) malloc(strlen(p) + 1);
108
if (c & ~0x7f) g->u.has_Latin1char = TRUE;
111
case 'n': case 'l': case 'r':
113
storeline(lp,line,*p,g);
124
if (line != lineptr) {
126
storeline(lp,line,'n',g);
132
unrecognized(node_t* n, char* p)
134
fprintf(stderr,"warning: node %s, port %s unrecognized\n",n->name,p);
137
#define GAP (PS2INCH(4.))
139
void poly_init(node_t *);
140
void poly_free(node_t *);
141
port_t poly_port(node_t*, char *);
142
int poly_inside(node_t*, pointf, edge_t *);
143
void poly_gencode(node_t*);
145
void record_init(node_t *);
146
void record_free(node_t *);
147
port_t record_port(node_t*, char *);
148
int record_inside(node_t*, pointf, edge_t *);
149
int record_path(node_t* n, edge_t* e, int pt, box rv[], int* kptr);
150
void record_gencode(node_t*);
152
void epsf_init(node_t *);
153
void epsf_free(node_t *);
154
int epsf_inside(node_t*, pointf, edge_t *);
155
void epsf_gencode(node_t*);
157
/* polygon descriptions. "polygon" with 0 sides takes all user control */
159
/* regul perip sides orien disto skew */
160
static polygon_t p_polygon = { FALSE, 1, 0, 0., 0., 0. };
161
/* builtin polygon descriptions */
162
static polygon_t p_ellipse = { FALSE, 1, 1, 0., 0., 0. };
163
static polygon_t p_circle = { TRUE, 1, 1, 0., 0., 0. };
164
static polygon_t p_egg = { FALSE, 1, 1, 0., -.3, 0. };
165
static polygon_t p_triangle = { FALSE, 1, 3, 0., 0., 0. };
166
static polygon_t p_box = { FALSE, 1, 4, 0., 0., 0. };
167
static polygon_t p_plaintext = { FALSE, 0, 4, 0., 0., 0. };
168
static polygon_t p_diamond = { FALSE, 1, 4, 45., 0., 0. };
169
static polygon_t p_trapezium = { FALSE, 1, 4, 0., -.4, 0. };
170
static polygon_t p_parallelogram = { FALSE, 1, 4, 0., 0., .6 };
171
static polygon_t p_house = { FALSE, 1, 5, 0., -.64, 0. };
172
static polygon_t p_hexagon = { FALSE, 1, 6, 0., 0., 0. };
173
static polygon_t p_octagon = { FALSE, 1, 8, 0., 0., 0. };
174
/* redundant and undocumented builtin polygons */
175
static polygon_t p_doublecircle = { TRUE, 2, 1, 0., 0., 0. };
176
static polygon_t p_invtriangle = { FALSE, 1, 3, 180., 0., 0. };
177
static polygon_t p_invtrapezium = { FALSE, 1, 4, 180., -.4, 0. };
178
static polygon_t p_invhouse = { FALSE, 1, 5, 180., -.64, 0. };
179
static polygon_t p_doubleoctagon = { FALSE, 2, 8, 0., 0., 0. };
180
static polygon_t p_tripleoctagon = { FALSE, 3, 8, 0., 0., 0. };
181
static polygon_t p_Mdiamond = { FALSE, 1, 4, 45., 0., 0. ,DIAGONALS|AUXLABELS};
182
static polygon_t p_Msquare = { TRUE, 1, 4, 0., 0., 0. ,DIAGONALS};
183
static polygon_t p_Mcircle = { TRUE, 1, 1, 0., 0., 0.,DIAGONALS|AUXLABELS};
185
static shape_desc Shapes[] = { /* first entry is default for no such shape */
186
{"box" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_box },
187
{"polygon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_polygon },
188
{"ellipse" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_ellipse },
189
{"circle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_circle },
190
{"egg" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_egg },
191
{"triangle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_triangle },
192
{"plaintext" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_plaintext },
193
{"diamond" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_diamond },
194
{"trapezium" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_trapezium },
195
{"parallelogram",poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_parallelogram },
196
{"house" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_house },
197
{"hexagon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_hexagon },
198
{"octagon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_octagon },
199
/* redundant and undocumented builtin polygons */
200
{"doublecircle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_doublecircle },
201
{"doubleoctagon",poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_doubleoctagon },
202
{"tripleoctagon",poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_tripleoctagon },
203
{"invtriangle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_invtriangle },
204
{"invtrapezium" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_invtrapezium },
205
{"invhouse" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_invhouse },
206
{"Mdiamond" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_Mdiamond },
207
{"Msquare" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_Msquare },
208
{"Mcircle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_Mcircle },
209
/* *** shapes other than polygons *** */
210
{"record" ,record_init,record_free,record_port,record_inside,record_path,record_gencode,NULL},
211
{"Mrecord" ,record_init,record_free,record_port,record_inside,record_path,record_gencode,NULL},
212
{"epsf" ,epsf_init,epsf_free,poly_port,epsf_inside,NULL,epsf_gencode,NULL},
213
{NULL ,NULL,NULL,NULL,NULL,NULL,NULL,NULL}
216
static double quant(double val, double q)
220
if (i * q + .00001 < val) i++;
224
void poly_init(node_t* n)
229
double temp,alpha,beta,gamma,delta,xb,yb;
230
double orientation,distortion,skew;
231
double sectorangle, sidelength, skewdist, gdistortion, gskew;
232
double angle, sinx, cosx, xmax, ymax, scalex, scaley;
233
int regular,peripheries,sides;
235
polygon_t *poly=NEW(polygon_t);
237
regular = n->u.shape->polygon->regular;
238
peripheries = n->u.shape->polygon->peripheries;
239
sides = n->u.shape->polygon->sides;
240
orientation = n->u.shape->polygon->orientation;
241
skew = n->u.shape->polygon->skew;
242
distortion = n->u.shape->polygon->distortion;
244
regular |= mapbool(agget(n,"regular"));
245
peripheries = late_int(n,N_peripheries,peripheries,0);
246
orientation += late_float(n,N_orientation,0.0,-360.0);
247
if (sides==0) { /* not for builtins */
248
skew = late_float(n,N_skew,0.0,-100.0);
249
sides = late_int(n,N_sides,4,0);
250
distortion = late_float(n,N_distortion,0.0,-100.0);
253
/* get label dimensions */
254
dimen = n->u.label->dimen;
256
if (mapbool(late_string(n,N_fixed,"false"))) {
257
if ((n->u.width < dimen.x) || (n->u.height < dimen.y))
258
fprintf(stderr,"dot: warning, node '%s' size too small for label\n",
260
dimen.x = dimen.y = 0;
264
if ((temp = n->graph->u.drawing->quantum) > 0.0) {
265
dimen.x = quant(dimen.x,temp);
266
dimen.y = quant(dimen.y,temp);
269
/* make square if necessary */
271
/* make x and y dimensions equal */
272
n->u.width = n->u.height = MIN(n->u.width,n->u.height);
273
xb = yb = MAX(dimen.x,dimen.y);
275
xb = dimen.x; yb = dimen.y;
279
/* I don't know how to distort or skew ellipses in postscript */
280
/* Convert request to a polygon with a large number of sides */
281
if ((sides<=2) && ((distortion!=0.) || (skew!=0.))) {
285
/* adjust bounding box so that label fits in inner ellipse */
286
/* this will change the symmetry of the bounding box */
287
/* adjust for inner to outer diameter of polygon */
288
if (sides>2) { /* except ellipses */
289
temp = cos(PI/sides);
290
xb /= temp; yb /= temp;
294
|| ((ROUND(orientation)%90)!=0)
297
if (yb>xb) temp = xb * (sqrt(2.) - 1.);
298
else temp = yb * (sqrt(2.) - 1.);
299
xb += temp; yb += temp;
301
xb=MAX(n->u.width,xb); yb=MAX(n->u.height,yb);
303
if (peripheries<1) outp=1;
304
if (sides<3) { /* ellipses */
306
vertices=N_NEW(outp,pointf);
307
P.x=xb/2.; P.y=yb/2.;
310
for (j=1; j<peripheries; j++) {
311
P.x += GAP; P.y += GAP;
314
xb=2.*P.x; yb=2.*P.y;
317
vertices=N_NEW(outp*sides,pointf);
318
sectorangle = 2.*PI/sides;
319
sidelength = sin(sectorangle/2.);
320
skewdist = hypot(fabs(distortion)+fabs(skew),1.);
321
gdistortion = distortion*sqrt(2.)/cos(sectorangle/2.);
323
angle = (sectorangle-PI)/2.;
324
sincos(angle,&sinx,&cosx);
325
R.x = .5*cosx; R.y = .5*sinx;
327
angle += (PI-sectorangle)/2.;
328
for (i=0; i<sides; i++) {
330
/*next regular vertex*/
331
angle += sectorangle;
332
sincos(angle,&sinx,&cosx);
333
R.x += sidelength*cosx; R.y += sidelength*sinx;
336
P.x = R.x*(skewdist+R.y*gdistortion)+R.y*gskew;
340
alpha = RADIANS(orientation)+atan2(P.y,P.x);
341
sincos(alpha,&sinx,&cosx);
342
P.x = P.y = hypot(P.x,P.y);
343
P.x *= cosx; P.y *= sinx;
346
P.x *= xb; P.y *= yb;
348
/*find max for bounding box*/
349
xmax = MAX(fabs(P.x),xmax); ymax = MAX(fabs(P.y),ymax);
351
/* store result in array of points */
355
/* apply minimum dimensions */
356
xmax *=2.; ymax *=2.;
357
xb=MAX(n->u.width,xmax); yb=MAX(n->u.height,ymax);
358
scalex=xb/xmax; scaley=yb/ymax;
360
for (i=0; i<sides; i++) {
362
P.x *= scalex; P.y *= scaley;
367
Q = vertices[(sides-1)];
369
beta = atan2(R.y-Q.y,R.x-Q.x);
370
for (i=0; i<sides; i++) {
372
/*for each vertex find the bisector*/
373
P = Q; Q = R; R = vertices[(i+1)%sides];
374
alpha = beta; beta = atan2(R.y-Q.y,R.x-Q.x);
375
gamma = (alpha+PI-beta)/2.;
377
/*find distance along bisector to*/
378
/*intersection of next periphery*/
379
temp = GAP/sin(gamma);
381
/*convert this distance to x and y*/
383
sincos(delta,&sinx,&cosx);
384
sinx *= temp; cosx *= temp;
386
/*save the vertices of all the*/
387
/*peripheries at this base vertex*/
388
for (j=1; j<peripheries; j++) {
389
Q.x += cosx; Q.y += sinx;
390
vertices[i+j*sides] = Q;
393
for (i=0; i<sides; i++) {
394
P = vertices[i+(peripheries-1)*sides];
395
xb = MAX(2.*fabs(P.x),xb);
396
yb = MAX(2.*fabs(P.y),yb);
400
poly->regular = regular;
401
poly->peripheries = peripheries;
403
poly->orientation = orientation;
405
poly->distortion = distortion;
406
poly->vertices = vertices;
410
n->u.shape_info = (void*) poly;
413
void poly_free(node_t *n)
415
polygon_t* p = n->u.shape_info;
423
int poly_inside(node_t* n, pointf p, edge_t* e)
425
static polygon_t *poly;
426
static int last,outp,sides;
427
static node_t *lastn;
429
static pointf *vertex;
430
static double xsize,ysize,scalex,scaley,box_URx,box_URy;
436
P = (n->graph->u.left_to_right? flip_ptf(p) : p);
438
poly = (polygon_t*) n->u.shape_info;
439
vertex = poly->vertices;
443
/* get point and node size adjusted for rankdir=LR */
444
if (n->graph->u.left_to_right) {
445
ysize = n->u.lw + n->u.rw; xsize = n->u.ht;
448
xsize = n->u.lw + n->u.rw; ysize = n->u.ht;
452
if (xsize == 0.0) xsize = 1.0;
453
if (ysize == 0.0) ysize = 1.0;
454
scalex = n->u.width/xsize; scaley = n->u.height/ysize;
455
box_URx = n->u.width/2.0; box_URy = n->u.height/2.0;
457
/* index to outer-periphery */
458
outp=(poly->peripheries-1)*sides;
463
P.x *= scalex; P.y *= scaley;
465
/* inside bounding box? */
466
if ((fabs(P.x)>box_URx) || (fabs(P.y)>box_URy)) return FALSE;
469
if (sides<=2) return (hypot(P.x/box_URx,P.y/box_URy)<1.);
471
/* use fast test in case we are converging on a segment */
472
i = last % sides; /*in case last left over from larger polygon*/
473
i1 = (i + 1) % sides;
474
Q = vertex[i+outp]; R = vertex[i1+outp];
475
if ( !(same_side(P,O,Q,R))) return FALSE;
476
if ( (s=same_side(P,Q,R,O)) && (same_side(P,R,O,Q))) return TRUE;
477
for (j = 1; j < sides; j++) {
479
i = i1; i1 = (i + 1) % sides;
481
i1 = i; i = (i + sides - 1) % sides;
483
if ( !(same_side(P,O,vertex[i+outp],vertex[i1+outp]))) {
488
last = i; /* in case next edge is to same side */
492
int same_side(pointf p0, pointf p1, pointf L0, pointf L1)
500
c = a * L0.x + b * L0.y;
502
s0 = (a*p0.x + b*p0.y - c >= 0);
503
s1 = (a*p1.x + b*p1.y - c >= 0);
509
poly_port(node_t* n, char* pname)
511
static char *points_of_compass[] =
512
{"n","ne","e","se","s","sw","w","nw",NULL};
513
static struct {char x,y;} a[] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};
520
if (*pname) pname++; /* skip over delim */
521
for (i = 0; (p = points_of_compass[i]); i++)
522
if (streq(p,pname)) break;
525
if (pname[0]) unrecognized(n,pname);
531
rv.p.x = a[i].x * wd;
532
rv.p.y = a[i].y * ht;
533
rv.order = (MC_SCALE * (n->u.lw + rv.p.x)) / (n->u.lw + n->u.rw);
534
rv.constrained = FALSE;
540
static void pencolor(node_t *n)
544
color = late_nnstring(n,N_color,"");
546
CodeGen->set_pencolor(color);
549
static void fillcolor(node_t *n)
553
color = late_nnstring(n,N_fillcolor,"");
555
/* for backward compatibilty, default fill is same as pen */
556
color = late_nnstring(n,N_color,"");
558
color = (Output_lang == MIF ? "black" : DEFAULT_FILL);
560
CodeGen->set_fillcolor(color);
563
static int stylenode(node_t* n)
566
char *style,**pstyle;
570
style = late_nnstring(n,N_style,"");
572
pstyle = parse_style(style);
573
CodeGen->set_style(pstyle);
574
for (i = 0; pstyle[i]; i++) {
575
if (strcmp(pstyle[i],"filled") == 0) {istyle |= FILLED; continue;}
576
if (strcmp(pstyle[i],"rounded") == 0) {istyle |= ROUNDED; continue;}
577
if (strcmp(pstyle[i],"diagonals") == 0) {istyle |= DIAGONALS; continue;}
580
if ((poly = n->u.shape->polygon)) istyle |= poly->option;
585
/* generic polygon gencode routine */
586
void poly_gencode(node_t* n)
590
int i,j,peripheries,sides,style;
596
poly = (polygon_t*) n->u.shape_info;
597
vertices = poly->vertices;
599
peripheries = poly->peripheries;
600
if (A_size < sides) {A_size = sides + 5; A = ALLOC(A_size,A,point);}
602
CodeGen->begin_node(n);
603
CodeGen->begin_context();
604
/* prescale by 16.0 to help rounding trick below */
605
xsize = ((n->u.lw + n->u.rw) / n->u.width) * 16.0;
606
ysize = ((n->u.ht) / n->u.height) * 16.0;
608
/* this is bad, but it's because of how the VRML driver works */
610
if ((CodeGen == &VRML_CodeGen) && (peripheries == 0)) {
615
style = stylenode(n);
616
pencolor(n); /* emit pen color */
617
if (style & FILLED) fillcolor(n); /* emit fill color */
618
for (j = 0; j < peripheries; j++) {
619
for (i = 0; i < sides; i++) {
620
P = vertices[i+j*sides];
621
/* simple rounding produces random results around .5
622
* this trick should clip off the random part.
623
* (note xsize/ysize prescaled by 16.0 above) */
624
A[i].x = ROUND(P.x * xsize) / 16;
625
A[i].y = ROUND(P.y * ysize) / 16;
626
if (sides > 2) {A[i].x += n->u.coord.x; A[i].y += n->u.coord.y;}
628
if (!j && (style & FILLED)) {
629
/* fill innermost periphery only */
635
if (find_user_shape(n->u.shape->name)) {
636
CodeGen->user_shape(n->u.shape->name,A,sides,filled);
638
else if (sides <= 2) {
639
CodeGen->ellipse(n->u.coord,A[0].x,A[0].y,filled);
640
if (style & DIAGONALS) {
644
else if (style & (ROUNDED | DIAGONALS)) {
645
round_corners(n,A,sides,style);
648
CodeGen->polygon(A,sides,filled);
652
if (style & AUXLABELS) Mlabel_hack(n);
653
n->u.label->p = n->u.coord;
654
emit_label(n->u.label,n->graph);
655
CodeGen->end_context();
660
hack1(node_t* n, char* str, int k)
666
fontsize = n->u.label->fontsize*.8; /* magic? */
668
p.x = n->u.coord.x - (strlen(str) * n->u.label->fontsize) / 2;
669
p.y = n->u.coord.y + (k * (n->u.ht - n->u.label->fontsize - 2)) / 2;
670
CodeGen->begin_context();
671
CodeGen->set_font(n->u.label->fontname,fontsize);
673
fake.width = strlen(str) * fontsize; /* ugh */
674
fake.just = 0; /* does this field do anything? SCN */
675
CodeGen->textline(p,&fake);
676
CodeGen->end_context();
679
void Mlabel_hack(node_t* n)
682
if ((str = agget(n,"toplabel"))) hack1(n,str,1);
683
if ((str = agget(n,"bottomlabel"))) hack1(n,str,-1);
686
void Mcircle_hack(node_t* n)
692
x = .6614; /* x^2 + y^2 = 1.0 */
693
p.y = y * n->u.ht / 2.0;
694
p.x = n->u.rw * x; /* assume node is symmetric */
696
A[0] = add_points(p,n->u.coord);
697
A[1].y = A[0].y; A[1].x = A[0].x - 2*p.x;
698
CodeGen->polyline(A,2);
699
A[0].y -= 2*p.y; A[1].y = A[0].y;
700
CodeGen->polyline(A,2);
704
/* the "record" shape is a rudimentary table formatter */
712
#define ISCTRL(c) ((c) == '{' || (c) == '}' || (c) == '|' || (c) == '<' || (c) == '>')
714
static char *reclblp;
717
parse_reclbl(node_t *n, int LR, int flag, char* text)
719
field_t *fp, *rv = NEW(field_t);
720
char *tsp, *psp, *hstsp, *hspsp, *sp;
722
int maxf, cnt, mode, wflag, ishardspace, fi;
725
for (maxf = 1, cnt = 0, sp = reclblp; *sp; sp++) {
728
if (*sp && (*sp == '{' || *sp == '}' || *sp == '|'))
735
else if (*sp == '|' && cnt == 0)
740
/*maxf = strccnt(reclblp, '|') + 1;*/
741
rv->fld = N_NEW (maxf, field_t*);
745
hstsp = tsp = text, hspsp = psp = &port[0];
751
if (mode & (HASTABLE | HASPORT))
753
mode |= (HASPORT | INPORT);
757
if (!(mode & INPORT))
764
if (mode != 0 || !*reclblp)
767
if (!(rv->fld[fi++] = parse_reclbl (n, NOT (LR) , FALSE, text)))
773
if ((!*reclblp && !flag) || (mode & INPORT))
775
if (!(mode & HASTABLE))
776
fp = rv->fld[fi++] = NEW (field_t);
777
if (mode & HASPORT) {
778
if (psp > &port[0] + 1 &&
783
fp->id = strdup (&port[0]);
784
hspsp = psp = &port[0];
786
if (!(mode & (HASTEXT | HASTABLE)))
787
mode |= HASTEXT, *tsp++ = ' ';
788
if (mode & HASTEXT) {
789
if (tsp > text + 1 &&
794
fp->lp = make_label (strdup (text), n->u.label->fontsize, n->u.label->fontname, n->u.label->fontcolor,n->graph);
799
if (*reclblp == '}') {
810
if (*(reclblp + 1)) {
811
if (ISCTRL (*(reclblp + 1)))
813
else if (*(reclblp + 1) == ' ')
814
ishardspace = TRUE, reclblp++;
816
/* falling through ... */
818
if ((mode & HASTABLE) && *reclblp != ' ')
820
if (!(mode & (INTEXT | INPORT)) && *reclblp != ' ')
821
mode |= (INTEXT | HASTEXT);
823
if (!(*reclblp == ' ' && !ishardspace &&
828
} else if (mode & INPORT) {
829
if (!(*reclblp == ' ' && !ishardspace &&
845
size_reclbl(node_t* n, field_t* f)
850
if (f->lp) d = cvt2pt(f->lp->dimen);
853
for (i = 0; i < f->n_flds; i++) {
854
d0 = size_reclbl(n,f->fld[i]);
855
if (f->LR) { d.x += d0.x; d.y = MAX(d.y,d0.y); }
856
else { d.y += d0.y; d.x = MAX(d.x,d0.x); }
864
resize_reclbl(field_t* f, point sz)
872
d.x = sz.x - f->size.x; d.y = sz.y - f->size.y;
875
/* adjust children */
877
if (f->LR) inc = (double)d.x/f->n_flds;
878
else inc = (double)d.y/f->n_flds;
879
for (i = 0; i < f->n_flds; i++) {
881
amt = ((int)((i+1)*inc)) - ((int)(i*inc));
882
if (f->LR) newsz = pointof(sf->size.x+amt,sz.y);
883
else newsz = pointof(sz.x,sf->size.y+amt);
884
resize_reclbl(sf,newsz);
890
pos_reclbl(field_t* f, point ul)
894
f->b.LL = pointof(ul.x,ul.y-f->size.y);
895
f->b.UR = pointof(ul.x+f->size.x,ul.y);
896
for (i = 0; i < f->n_flds; i++) {
897
pos_reclbl(f->fld[i],ul);
898
if (f->LR) ul.x = ul.x + f->fld[i]->size.x;
899
else ul.y = ul.y - f->fld[i]->size.y;
903
/* syntax of labels: foo|bar|baz or foo|(recursive|label)|baz */
905
record_init(node_t* n)
910
char* textbuf; /* temp buffer for storing labels */
912
reclblp = n->u.label->text;
913
len = strlen (reclblp);
914
textbuf = N_NEW(len+1,char);
915
if (!(info = parse_reclbl(n,NOT(n->graph->u.left_to_right), TRUE, textbuf))) {
916
fprintf (stderr, "bad label format %s\n", n->u.label->text);
918
info = parse_reclbl(n,NOT(n->graph->u.left_to_right), TRUE, textbuf);
922
sz.x = POINTS(n->u.width); sz.y = POINTS(n->u.height);
923
sz.x = MAX(info->size.x,sz.x); sz.y = MAX(info->size.y,sz.y);
924
resize_reclbl(info,sz);
925
ul = pointof(-sz.x/2,sz.y/2);
927
n->u.width = PS2INCH(info->size.x);
928
n->u.height = PS2INCH(info->size.y);
929
n->u.shape_info = (void*) info;
933
record_free(node_t* n)
935
field_t* p = n->u.shape_info;
941
map_rec_port(field_t* f, char* str)
946
if (f->id && (strcmp(f->id,str) == 0)) rv = f;
949
for (sub = 0; sub < f->n_flds; sub++)
950
if ((rv = map_rec_port(f->fld[sub],str))) break;
956
record_port(node_t* n, char* pname)
962
if (pname[0] != ':') return Center; /*could be '\000' */
963
if ((f = map_rec_port((field_t*) n->u.shape_info,pname+1)) == NULL) {
964
unrecognized(n,pname);
969
rv.p = pointof((b.LL.x+b.UR.x)/2,(b.LL.y+b.UR.y)/2);
970
if (n->graph->u.left_to_right) rv.p = invflip_pt(rv.p);
971
rv.order = (MC_SCALE * (n->u.lw + rv.p.x)) / (n->u.lw + n->u.rw);
972
rv.constrained = FALSE;
978
record_inside(node_t* n, pointf p, edge_t* e)
985
static edge_t *last_e;
986
static node_t *last_n;
989
if (n->graph->u.left_to_right) p = flip_ptf(p);
990
for (f = e; f->u.edge_type != NORMAL; f = f->u.to_orig);
992
if ((e != last_e) || (n != last_n)) {
993
last_e = e; last_n = n;
994
pname = agget(e,(n == f->head ? "headport" : "tailport"));
995
fld = map_rec_port((field_t*)n->u.shape_info,pname+1);
999
fld0 = (field_t*) n->u.shape_info;
1000
UR.x = fld0->size.x / 2.0; LL.x = -UR.x;
1001
UR.y = fld0->size.y / 2.0; LL.y = -UR.y;
1004
LL.x = fld->b.LL.x; LL.y = fld->b.LL.y;
1005
UR.x = fld->b.UR.x; UR.y = fld->b.UR.y;
1007
return (BETWEEN(LL.x,p.x,UR.x) && BETWEEN(LL.y,p.y,UR.y));
1010
static box flip_rec_box(box b, point p)
1014
rv.UR.x = b.UR.y; rv.UR.y = b.UR.x;
1015
rv.LL.x = b.LL.y; rv.LL.y = b.LL.x;
1017
rv.LL.x += p.x; rv.LL.y += p.y;
1018
rv.UR.x += p.x; rv.UR.y += p.y;
1024
record_path(node_t* n, edge_t* e, int pt, box rv[], int* kptr)
1030
if (pt == 1) p = e->u.tail_port.p;
1031
else p = e->u.head_port.p;
1032
info = (field_t*) n->u.shape_info;
1034
for (i = 0; i < info->n_flds; i++) {
1035
if (n->graph->u.left_to_right == FALSE)
1036
{ ls = info->fld[i]->b.LL.x; rs = info->fld[i]->b.UR.x; }
1038
{ ls = info->fld[i]->b.LL.y; rs = info->fld[i]->b.UR.y; }
1039
if (BETWEEN(ls,p.x,rs)) {
1040
/* FIXME: I don't understand this code */
1041
if (n->graph->u.left_to_right) {
1042
rv[0] = flip_rec_box(info->fld[i]->b,n->u.coord);
1045
rv[0].LL.x = n->u.coord.x + ls;
1046
rv[0].LL.y = n->u.coord.y - n->u.ht/2;
1047
rv[0].UR.x = n->u.coord.x + rs;
1050
s0 = (rv[0].UR.x - rv[0].LL.x)/6;
1051
s0 = MIN(s0,n->graph->u.nodesep);
1052
s1 = MIN(p.x - rv[0].LL.x,rv[0].UR.x - p.x)/2;
1057
rv[0].UR.y = n->u.coord.y + n->u.ht/2;
1062
if (pt == 1) side = BOTTOM; else side = TOP;
1067
gen_fields(node_t* n, field_t* f)
1074
cx = (f->b.LL.x + f->b.UR.x)/2.0 + n->u.coord.x;
1075
cy = (f->b.LL.y + f->b.UR.y)/2.0 + n->u.coord.y;
1076
f->lp->p = pointof((int)cx,(int)cy);
1077
emit_label(f->lp,n->graph);
1080
/* yes it is ridiculous that black is hardwired here, the same way
1081
* it is wired into psgen.c ... outline color should be adjustable */
1082
/* for reasons not presently remembered, we used to say
1083
CodeGen->set_color("black");
1086
for (i = 0; i < f->n_flds; i++) {
1089
A[0] = f->fld[i]->b.LL;
1091
A[1].y = f->fld[i]->b.UR.y;
1094
A[1] = f->fld[i]->b.UR;
1095
A[0].x = f->fld[i]->b.LL.x;
1098
A[0] = add_points(A[0],n->u.coord);
1099
A[1] = add_points(A[1],n->u.coord);
1100
CodeGen->polyline(A,2);
1102
gen_fields(n,f->fld[i]);
1107
record_gencode(node_t* n)
1113
f = (field_t*) n->u.shape_info;
1116
A[1].x = A[2].x; A[1].y = A[0].y;
1117
A[3].x = A[0].x; A[3].y = A[2].y;
1118
for (i = 0; i < 4; i++) A[i] = add_points(A[i],n->u.coord);
1119
CodeGen->begin_node(n);
1120
CodeGen->begin_context();
1121
style = stylenode(n);
1123
if (style & FILLED) fillcolor(n); /* emit fill color */
1124
if (streq(n->u.shape->name,"Mrecord")) style |= ROUNDED;
1125
if (style & (ROUNDED | DIAGONALS)) round_corners(n,A,4,ROUNDED);
1126
else CodeGen->polygon(A,4,style&FILLED);
1128
CodeGen->end_context();
1129
CodeGen->end_node();
1132
static shape_desc **UserShape;
1133
static int N_UserShape;
1136
find_user_shape(char* name)
1140
for (i = 0; i < N_UserShape; i++) {
1141
if (streq(UserShape[i]->name,name)) return UserShape[i];
1148
user_shape(char* name)
1153
if ((p = find_user_shape(name))) return p;
1155
UserShape = ALLOC(N_UserShape,UserShape,shape_desc*);
1156
p = UserShape[i] = NEW(shape_desc);
1159
if (Lib == NULL) fprintf(stderr, "warning: using %s for unknown shape %s\n", Shapes[0].name,p->name);
1164
bind_shape(char* name)
1166
shape_desc *ptr,*rv= NULL;
1168
for (ptr = Shapes; ptr->name; ptr++)
1169
if (!strcmp(ptr->name,name)) {rv = ptr; break;}
1170
if (rv == NULL) rv = user_shape(name);
1175
interpolate(double t, point p0, point p1)
1178
rv.x = p0.x + t * (p1.x - p0.x);
1179
rv.y = p0.y + t * (p1.y - p0.y);
1184
round_corners(node_t *nn, point *A, int n, int style)
1186
point *B,C[2],p0,p1;
1190
if (style & DIAGONALS) mode = DIAGONALS;
1191
else mode = ROUNDED;
1192
B = N_NEW(4*n+4,point);
1194
for (seg = 0; seg < n; seg++) {
1196
if (seg < n - 1) p1 = A[seg+1];
1200
d = sqrt(dx*dx + dy*dy);
1201
/*t = ((mode == ROUNDED)? RBCONST / d : .5);*/
1203
if (mode != ROUNDED) B[i++] = p0;
1204
if (mode == ROUNDED) B[i++] = interpolate(RBCURVE*t,p0,p1);
1205
B[i++] = interpolate(t,p0,p1);
1206
B[i++] = interpolate(1.0 - t,p0,p1);
1207
if (mode == ROUNDED) B[i++] = interpolate(1.0 - RBCURVE*t,p0,p1);
1213
if (mode == ROUNDED) {
1214
for (seg = 0; seg < n; seg++) {
1215
CodeGen->polyline(B + 4*seg+1,2);
1216
CodeGen->beziercurve(B + 4*seg+2,4,FALSE,FALSE);
1219
else { /* diagonals are weird. rewrite someday. */
1221
if (style & FILLED) fillcolor(nn); /* emit fill color */
1222
CodeGen->polygon(A,n,style&FILLED);
1223
for (seg = 0; seg < n; seg++) {
1225
C[0] = B[3 * seg]; C[1] = B[3 * seg + 3];
1226
CodeGen->polyline(C,2);
1228
C[0] = B[3 * seg + 2]; C[1] = B[3 * seg + 4];
1229
CodeGen->polyline(C,2);
1236
draw_user_shape(node_t *n, point *A, int sides, int filled)
1241
epsf_inside(node_t* n, pointf p, edge_t* e)
1246
P = (n->graph->u.left_to_right? flip_ptf(p) : p);
1248
return ((P.y >= -x2) && (P.y <= x2) && (P.x >= -n->u.lw) && (P.x <= n->u.rw));