~ubuntu-branches/ubuntu/lucid/graphviz/lucid-security

« back to all changes in this revision

Viewing changes to dotneato/common/shapes.c

  • Committer: Bazaar Package Importer
  • Author(s): Stephen M Moraco
  • Date: 2002-02-05 18:52:12 UTC
  • Revision ID: james.westby@ubuntu.com-20020205185212-8i04c70te00rc40y
Tags: upstream-1.7.16
ImportĀ upstreamĀ versionĀ 1.7.16

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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.
 
9
*/
 
10
#pragma prototyped
 
11
 
 
12
/*
 
13
 * every shape has these functions:
 
14
 *
 
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)
 
19
 *                      of a port.
 
20
 * int          SHAPE_inside(node_t *n, pointf p, edge_t *e);
 
21
 *                      test if point is inside the node shape which is
 
22
 *                      assumed convex.
 
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,
 
29
 *                      return side
 
30
 *
 
31
 * some shapes, polygons in particular, use additional shape control data *
 
32
 *
 
33
 */
 
34
 
 
35
 /* this needs an overhaul */
 
36
 
 
37
#include        "render.h"
 
38
 
 
39
#ifdef DMALLOC
 
40
#include "dmalloc.h"
 
41
#endif
 
42
 
 
43
#define FILLED  (1 << 0)
 
44
#define ROUNDED (1 << 1)
 
45
#define DIAGONALS (1 << 2)
 
46
#define AUXLABELS (1 << 3)
 
47
#define RBCONST 12
 
48
#define RBCURVE .5
 
49
 
 
50
#ifndef HAVE_SINCOS
 
51
void sincos(x,s,c) double x,*s,*c; { *s = sin(x); *c = cos(x); }
 
52
#else
 
53
extern void sincos(double x, double *s, double *c);
 
54
#endif
 
55
 
 
56
static port_t   Center = { {0,0}, -1, 0, 0, 0};
 
57
 
 
58
#if 0 /* not used */
 
59
static point
 
60
flip_pt(point p)
 
61
{ int           t = p.x; p.x = -p.y; p.y = t; return p; }
 
62
#endif
 
63
 
 
64
static point
 
65
invflip_pt(point p)
 
66
{ int           t = p.x; p.x = p.y; p.y = -t; return p; }
 
67
 
 
68
static pointf
 
69
flip_ptf(pointf p)
 
70
{ double        t = p.x; p.x = -p.y; p.y = t; return p; }
 
71
 
 
72
static void
 
73
storeline(textlabel_t *lp, char *line, char terminator,graph_t *g)
 
74
{
 
75
        double  width = 0.0;
 
76
 
 
77
        lp->line = ALLOC(lp->nlines+2,lp->line,textline_t);
 
78
        lp->line[lp->nlines].str = line;
 
79
        if (line) {
 
80
                if (CodeGen && CodeGen->textsize)
 
81
                        width = (CodeGen->textsize(line, lp->fontname,lp->fontsize).x);
 
82
                else
 
83
                        width = estimate_textsize(line, lp->fontname,lp->fontsize).x;
 
84
        }
 
85
        lp->line[lp->nlines].width = width;
 
86
        lp->line[lp->nlines].just = terminator;
 
87
        lp->nlines++;
 
88
        if (width > 0) {
 
89
                width += lp->fontsize;  /* margins */
 
90
                width = PS2INCH(width);
 
91
                if (lp->dimen.x < width) lp->dimen.x = width;
 
92
        }
 
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);
 
96
}
 
97
 
 
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)
 
100
{
 
101
        char            c,*p,*line,*lineptr;
 
102
 
 
103
        line = lineptr = NULL;
 
104
        p = str;
 
105
        line = lineptr = (char*) malloc(strlen(p) + 1);
 
106
        *line = 0;
 
107
        while ((c = *p++)) {
 
108
                if (c & ~0x7f) g->u.has_Latin1char = TRUE;
 
109
                if (c == '\\') {
 
110
                        switch (*p) {
 
111
                                case 'n': case 'l': case 'r':
 
112
                                        *lineptr++ = '\0';
 
113
                                        storeline(lp,line,*p,g);
 
114
                                        line = lineptr;
 
115
                                        break;
 
116
                                default:
 
117
                                        *lineptr++ = *p;
 
118
                        }
 
119
                        if (*p) p++;
 
120
                }
 
121
                else *lineptr++ = c;
 
122
        }
 
123
 
 
124
        if (line != lineptr) {
 
125
                *lineptr++ = '\0';
 
126
                storeline(lp,line,'n',g);
 
127
        }
 
128
        return lp->dimen;
 
129
}
 
130
 
 
131
static void
 
132
unrecognized(node_t* n, char* p)
 
133
{
 
134
        fprintf(stderr,"warning: node %s, port %s unrecognized\n",n->name,p);
 
135
}
 
136
 
 
137
#define GAP (PS2INCH(4.))
 
138
 
 
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*);
 
144
 
 
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*);
 
151
 
 
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*);
 
156
 
 
157
/* polygon descriptions.  "polygon" with 0 sides takes all user control */
 
158
 
 
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};
 
184
 
 
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}
 
214
};
 
215
 
 
216
static double quant(double val, double q)
 
217
{
 
218
        int     i;
 
219
        i = val / q;
 
220
        if (i * q + .00001 < val) i++;
 
221
        return i * q;
 
222
}
 
223
 
 
224
void poly_init(node_t* n)
 
225
{
 
226
        pointf  dimen;
 
227
        pointf  P,Q,R;
 
228
        pointf  *vertices;
 
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;
 
234
        int     i,j,outp;
 
235
        polygon_t *poly=NEW(polygon_t);
 
236
 
 
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;
 
243
 
 
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);
 
251
        }
 
252
 
 
253
        /* get label dimensions */
 
254
        dimen = n->u.label->dimen;
 
255
 
 
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",
 
259
                                n->name);
 
260
                dimen.x = dimen.y = 0;
 
261
        }
 
262
 
 
263
        /* quantization */
 
264
        if ((temp = n->graph->u.drawing->quantum) > 0.0) {
 
265
                dimen.x = quant(dimen.x,temp);
 
266
                dimen.y = quant(dimen.y,temp);
 
267
        }
 
268
 
 
269
        /* make square if necessary */
 
270
        if (regular) {
 
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);
 
274
        } else {
 
275
                xb = dimen.x; yb = dimen.y;
 
276
        }
 
277
 
 
278
 
 
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.))) {
 
282
                sides = 120;
 
283
        }
 
284
 
 
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;
 
291
        }
 
292
 
 
293
        if (  (sides!=4)
 
294
           || ((ROUND(orientation)%90)!=0)
 
295
           || (distortion!=0.)
 
296
           || (skew!=0.) ) {
 
297
                if (yb>xb) temp = xb * (sqrt(2.) - 1.);
 
298
                else temp = yb * (sqrt(2.) - 1.);
 
299
                xb += temp; yb += temp;
 
300
        }
 
301
        xb=MAX(n->u.width,xb); yb=MAX(n->u.height,yb);
 
302
        outp=peripheries;
 
303
        if (peripheries<1) outp=1;
 
304
        if (sides<3) { /* ellipses */
 
305
                sides=1;
 
306
                vertices=N_NEW(outp,pointf);
 
307
                P.x=xb/2.; P.y=yb/2.;
 
308
                vertices[0] = P;
 
309
                if (peripheries>1) {
 
310
                        for (j=1; j<peripheries; j++) {
 
311
                                P.x += GAP; P.y += GAP;
 
312
                                vertices[j] = P;
 
313
                        }
 
314
                        xb=2.*P.x; yb=2.*P.y;
 
315
                }
 
316
        } else {
 
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.);
 
322
                gskew = skew/2.;
 
323
                angle = (sectorangle-PI)/2.;
 
324
                sincos(angle,&sinx,&cosx);
 
325
                R.x = .5*cosx; R.y = .5*sinx;
 
326
                xmax=ymax=0.;
 
327
                angle += (PI-sectorangle)/2.;
 
328
                for (i=0; i<sides; i++) {
 
329
        
 
330
                        /*next regular vertex*/
 
331
                        angle += sectorangle;
 
332
                        sincos(angle,&sinx,&cosx);
 
333
                        R.x += sidelength*cosx; R.y += sidelength*sinx;
 
334
        
 
335
                        /*distort and skew*/
 
336
                        P.x = R.x*(skewdist+R.y*gdistortion)+R.y*gskew;
 
337
                        P.y = R.y;
 
338
        
 
339
                        /*orient P.x,P.y*/
 
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;
 
344
 
 
345
                        /*scale for label*/
 
346
                        P.x *= xb; P.y *= yb;
 
347
        
 
348
                        /*find max for bounding box*/
 
349
                        xmax = MAX(fabs(P.x),xmax); ymax = MAX(fabs(P.y),ymax);
 
350
        
 
351
                        /* store result in array of points */
 
352
                        vertices[i] = P;
 
353
                }
 
354
 
 
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;
 
359
        
 
360
                for (i=0; i<sides; i++) {
 
361
                        P = vertices[i];
 
362
                        P.x *= scalex; P.y *= scaley;
 
363
                        vertices[i] = P;
 
364
                }
 
365
        
 
366
                if (peripheries>1) {
 
367
                        Q = vertices[(sides-1)];
 
368
                        R = vertices[0];
 
369
                        beta = atan2(R.y-Q.y,R.x-Q.x);
 
370
                        for (i=0; i<sides; i++) {
 
371
 
 
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.;
 
376
 
 
377
                                /*find distance along bisector to*/
 
378
                                /*intersection of next periphery*/
 
379
                                temp = GAP/sin(gamma);
 
380
 
 
381
                                /*convert this distance to x and y*/
 
382
                                delta = alpha-gamma;
 
383
                                sincos(delta,&sinx,&cosx);
 
384
                                sinx *= temp; cosx *= temp;
 
385
 
 
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;
 
391
                                }
 
392
                        }
 
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);
 
397
                        }
 
398
                }
 
399
        }
 
400
        poly->regular = regular;
 
401
        poly->peripheries = peripheries;
 
402
        poly->sides = sides;
 
403
        poly->orientation = orientation;
 
404
        poly->skew = skew;
 
405
        poly->distortion = distortion;
 
406
        poly->vertices = vertices;
 
407
 
 
408
        n->u.width = xb;
 
409
        n->u.height = yb;
 
410
        n->u.shape_info = (void*) poly;
 
411
}
 
412
 
 
413
void poly_free(node_t *n)
 
414
{
 
415
        polygon_t* p = n->u.shape_info;
 
416
 
 
417
        if (p) {
 
418
                free(p->vertices);
 
419
                free(p);
 
420
        }
 
421
}
 
422
 
 
423
int poly_inside(node_t* n, pointf p, edge_t* e)
 
424
{
 
425
        static polygon_t *poly;
 
426
        static int      last,outp,sides;
 
427
        static node_t   *lastn;
 
428
        static pointf   O;
 
429
        static pointf   *vertex;
 
430
        static double   xsize,ysize,scalex,scaley,box_URx,box_URy;
 
431
 
 
432
        int             i,i1,j,s;
 
433
        pointf          P,Q,R;
 
434
 
 
435
        e = e;
 
436
        P = (n->graph->u.left_to_right? flip_ptf(p) : p);
 
437
        if (n != lastn) {
 
438
                poly = (polygon_t*) n->u.shape_info;
 
439
                vertex = poly->vertices;
 
440
                sides = poly->sides;
 
441
                lastn = n;
 
442
 
 
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;
 
446
                }
 
447
                else {
 
448
                        xsize = n->u.lw + n->u.rw; ysize = n->u.ht;
 
449
                }
 
450
 
 
451
                /* scale */
 
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;
 
456
 
 
457
                /* index to outer-periphery */
 
458
                outp=(poly->peripheries-1)*sides;
 
459
                if (outp<0) outp=0;
 
460
        }
 
461
 
 
462
        /* scale */
 
463
        P.x *= scalex; P.y *= scaley;
 
464
 
 
465
        /* inside bounding box? */
 
466
        if ((fabs(P.x)>box_URx) || (fabs(P.y)>box_URy)) return FALSE;
 
467
 
 
468
        /* ellipses */
 
469
        if (sides<=2) return (hypot(P.x/box_URx,P.y/box_URy)<1.);
 
470
 
 
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++) {
 
478
                if (s) {
 
479
                        i = i1; i1 = (i + 1) % sides;
 
480
                } else {
 
481
                        i1 = i; i = (i + sides - 1) % sides;
 
482
                } 
 
483
                if ( !(same_side(P,O,vertex[i+outp],vertex[i1+outp]))) {
 
484
                        last = i;
 
485
                        return FALSE;
 
486
                }
 
487
        }
 
488
        last = i;  /* in case next edge is to same side */
 
489
        return TRUE;
 
490
}
 
491
 
 
492
int same_side(pointf p0, pointf p1, pointf L0, pointf L1)
 
493
{
 
494
        int s0,s1;
 
495
        double a,b,c;
 
496
 
 
497
        /* a x + b y = c */
 
498
        a = -(L1.y - L0.y);
 
499
        b = (L1.x - L0.x);
 
500
        c = a * L0.x + b * L0.y;
 
501
 
 
502
        s0 = (a*p0.x + b*p0.y - c >= 0);
 
503
        s1 = (a*p1.x + b*p1.y - c >= 0);
 
504
        return (s0 == s1);
 
505
}
 
506
 
 
507
 
 
508
port_t
 
509
poly_port(node_t* n, char* pname)
 
510
{
 
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}};
 
514
 
 
515
        int             i,ht,wd;
 
516
        port_t  rv;
 
517
        char    *p;
 
518
 
 
519
 
 
520
        if (*pname) pname++; /* skip over delim */
 
521
        for (i = 0; (p = points_of_compass[i]); i++)
 
522
                if (streq(p,pname)) break;
 
523
 
 
524
        if (p == NULL) {
 
525
                if (pname[0]) unrecognized(n,pname);
 
526
                rv = Center;
 
527
        }
 
528
        else {
 
529
                ht = n->u.ht / 2; 
 
530
                wd = n->u.lw;
 
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;
 
535
                rv.defined = TRUE;
 
536
        }
 
537
        return rv;
 
538
}
 
539
 
 
540
static void pencolor(node_t *n)
 
541
{
 
542
        char *color;
 
543
 
 
544
        color = late_nnstring(n,N_color,"");
 
545
        if (color[0]) 
 
546
                CodeGen->set_pencolor(color);
 
547
}
 
548
 
 
549
static void fillcolor(node_t *n)
 
550
{
 
551
        char *color;
 
552
        
 
553
        color = late_nnstring(n,N_fillcolor,"");
 
554
        if (! color[0]) {
 
555
                /* for backward compatibilty, default fill is same as pen */
 
556
                color = late_nnstring(n,N_color,"");
 
557
                if (! color[0])
 
558
                        color = (Output_lang == MIF ? "black" : DEFAULT_FILL);
 
559
        }
 
560
        CodeGen->set_fillcolor(color);
 
561
}
 
562
 
 
563
static int stylenode(node_t* n)
 
564
{
 
565
        int             i;
 
566
        char            *style,**pstyle;
 
567
        int             istyle = 0;
 
568
        polygon_t       *poly;
 
569
 
 
570
        style = late_nnstring(n,N_style,"");
 
571
        if (style[0]) {
 
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;}
 
578
                }
 
579
        }
 
580
        if ((poly = n->u.shape->polygon)) istyle |= poly->option;
 
581
        return istyle;
 
582
}
 
583
 
 
584
 
 
585
/* generic polygon gencode routine */
 
586
void poly_gencode(node_t* n)
 
587
{
 
588
        polygon_t               *poly;
 
589
        double                  xsize, ysize;
 
590
        int                     i,j,peripheries,sides,style;
 
591
        pointf                  P,*vertices;
 
592
        static point    *A;
 
593
        static int              A_size;
 
594
        int                     filled;
 
595
        
 
596
        poly = (polygon_t*) n->u.shape_info;
 
597
        vertices = poly->vertices;
 
598
        sides = poly->sides;
 
599
        peripheries = poly->peripheries;
 
600
        if (A_size < sides) {A_size = sides + 5; A = ALLOC(A_size,A,point);}
 
601
 
 
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;
 
607
 
 
608
        /* this is bad, but it's because of how the VRML driver works */
 
609
#ifdef HAVE_LIBPNG
 
610
        if ((CodeGen == &VRML_CodeGen) && (peripheries == 0)) {
 
611
                peripheries = 1;
 
612
        }
 
613
#endif
 
614
 
 
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;}
 
627
                }
 
628
                if (!j && (style & FILLED)) {
 
629
                        /* fill innermost periphery only */
 
630
                        filled = 1;
 
631
                }
 
632
                else {
 
633
                        filled = 0;
 
634
                }
 
635
                if (find_user_shape(n->u.shape->name)) {
 
636
                        CodeGen->user_shape(n->u.shape->name,A,sides,filled);
 
637
                }
 
638
                else if (sides <= 2) {
 
639
                        CodeGen->ellipse(n->u.coord,A[0].x,A[0].y,filled);
 
640
                        if (style & DIAGONALS) {
 
641
                                Mcircle_hack(n);
 
642
                        }
 
643
                }
 
644
                else if (style & (ROUNDED | DIAGONALS)) {
 
645
                        round_corners(n,A,sides,style);
 
646
                }
 
647
                else {
 
648
                        CodeGen->polygon(A,sides,filled);
 
649
                }
 
650
        }
 
651
 
 
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();
 
656
        CodeGen->end_node();
 
657
}
 
658
 
 
659
static void
 
660
hack1(node_t* n, char* str, int k)
 
661
{
 
662
        point           p;
 
663
        double          fontsize;
 
664
        textline_t      fake;
 
665
 
 
666
        fontsize = n->u.label->fontsize*.8;     /* magic? */
 
667
 
 
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);
 
672
        fake.str = str;
 
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();
 
677
}
 
678
 
 
679
void Mlabel_hack(node_t* n)
 
680
{
 
681
        char            *str;
 
682
        if ((str = agget(n,"toplabel"))) hack1(n,str,1);
 
683
        if ((str = agget(n,"bottomlabel"))) hack1(n,str,-1);
 
684
}
 
685
 
 
686
void Mcircle_hack(node_t* n)
 
687
{
 
688
        float   x,y;
 
689
        point   A[2],p;
 
690
 
 
691
        y = .7500;
 
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 */
 
695
 
 
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);
 
701
}
 
702
 
 
703
 
 
704
/* the "record" shape is a rudimentary table formatter */
 
705
 
 
706
#define HASTEXT 1
 
707
#define HASPORT 2
 
708
#define HASTABLE 4
 
709
#define INTEXT 8
 
710
#define INPORT 16
 
711
 
 
712
#define ISCTRL(c) ((c) == '{' || (c) == '}' || (c) == '|' || (c) == '<' || (c) == '>')
 
713
 
 
714
static char *reclblp;
 
715
 
 
716
static field_t  *
 
717
parse_reclbl(node_t *n, int LR, int flag, char* text)
 
718
{
 
719
        field_t *fp, *rv = NEW(field_t);
 
720
        char *tsp, *psp, *hstsp, *hspsp, *sp;
 
721
        char port[SMALLBUF];
 
722
        int maxf, cnt, mode, wflag, ishardspace, fi;
 
723
 
 
724
        fp = NULL;
 
725
        for (maxf = 1, cnt = 0, sp = reclblp; *sp; sp++) {
 
726
                if (*sp == '\\') {
 
727
                        sp++;
 
728
                        if (*sp && (*sp == '{' || *sp == '}' || *sp == '|'))
 
729
                                continue;
 
730
                }
 
731
                if (*sp == '{')
 
732
                        cnt++;
 
733
                else if (*sp == '}')
 
734
                        cnt--;
 
735
                else if (*sp == '|' && cnt == 0)
 
736
                        maxf++;
 
737
                if (cnt < 0)
 
738
                        break;
 
739
        }
 
740
        /*maxf = strccnt(reclblp, '|') + 1;*/
 
741
        rv->fld = N_NEW (maxf, field_t*);
 
742
        rv->LR = LR;
 
743
        mode = 0;
 
744
        fi = 0;
 
745
        hstsp = tsp = text, hspsp = psp = &port[0];
 
746
        wflag = TRUE;
 
747
        ishardspace = FALSE;
 
748
        while (wflag) {
 
749
                switch (*reclblp) {
 
750
                case '<':
 
751
                        if (mode & (HASTABLE | HASPORT))
 
752
                                return NULL;
 
753
                        mode |= (HASPORT | INPORT);
 
754
                        reclblp++;
 
755
                        break;
 
756
                case '>':
 
757
                        if (!(mode & INPORT))
 
758
                                return NULL;
 
759
                        mode &= ~INPORT;
 
760
                        reclblp++;
 
761
                        break;
 
762
                case '{':
 
763
                        reclblp++;
 
764
                        if (mode != 0 || !*reclblp)
 
765
                                return NULL;
 
766
                        mode = HASTABLE;
 
767
                        if (!(rv->fld[fi++] = parse_reclbl (n, NOT (LR) , FALSE, text)))
 
768
                                return NULL;
 
769
                        break;
 
770
                case '}':
 
771
                case '|':
 
772
                case '\000':
 
773
                        if ((!*reclblp && !flag) || (mode & INPORT))
 
774
                                return NULL;
 
775
                        if (!(mode & HASTABLE))
 
776
                                fp = rv->fld[fi++] = NEW (field_t);
 
777
                        if (mode & HASPORT) {
 
778
                                if (psp > &port[0] + 1 &&
 
779
                                                psp - 1 != hspsp &&
 
780
                                                *(psp - 1) == ' ')
 
781
                                        psp--;
 
782
                                *psp = '\000';
 
783
                                fp->id = strdup (&port[0]);
 
784
                                hspsp = psp = &port[0];
 
785
                        }
 
786
                        if (!(mode & (HASTEXT | HASTABLE)))
 
787
                                mode |= HASTEXT, *tsp++ = ' ';
 
788
                        if (mode & HASTEXT) {
 
789
                                if (tsp > text + 1 &&
 
790
                                                tsp - 1 != hstsp &&
 
791
                                                *(tsp - 1) == ' ')
 
792
                                        tsp--;
 
793
                                *tsp = '\000';
 
794
                                fp->lp = make_label (strdup (text), n->u.label->fontsize, n->u.label->fontname, n->u.label->fontcolor,n->graph);
 
795
                                fp->LR = TRUE;
 
796
                                hstsp = tsp = text;
 
797
                        }
 
798
                        if (*reclblp) {
 
799
                                if (*reclblp == '}') {
 
800
                                        reclblp++;
 
801
                                        rv->n_flds = fi;
 
802
                                        return rv;
 
803
                                }
 
804
                                mode = 0;
 
805
                                reclblp++;
 
806
                        } else
 
807
                                wflag = FALSE;
 
808
                        break;
 
809
                case '\\':
 
810
                        if (*(reclblp + 1)) {
 
811
                                if (ISCTRL (*(reclblp + 1)))
 
812
                                        reclblp++;
 
813
                                else if (*(reclblp + 1) == ' ')
 
814
                                        ishardspace = TRUE, reclblp++;
 
815
                        }
 
816
                        /* falling through ... */
 
817
                default:
 
818
                        if ((mode & HASTABLE) && *reclblp != ' ')
 
819
                                return NULL;
 
820
                        if (!(mode & (INTEXT | INPORT)) && *reclblp != ' ')
 
821
                                mode |= (INTEXT | HASTEXT);
 
822
                        if (mode & INTEXT) {
 
823
                                if (!(*reclblp == ' ' && !ishardspace &&
 
824
                                                *(tsp - 1) == ' '))
 
825
                                        *tsp++ = *reclblp;
 
826
                                if (ishardspace)
 
827
                                        hstsp = tsp - 1;
 
828
                        } else if (mode & INPORT) {
 
829
                                if (!(*reclblp == ' ' && !ishardspace &&
 
830
                                                (psp == &port[0] ||
 
831
                                                *(psp - 1) == ' ')))
 
832
                                        *psp++ = *reclblp;
 
833
                                if (ishardspace)
 
834
                                        hspsp = psp - 1;
 
835
                        }
 
836
                        reclblp++;
 
837
                        break;
 
838
                }
 
839
        }
 
840
        rv->n_flds = fi;
 
841
        return rv;
 
842
}
 
843
 
 
844
point
 
845
size_reclbl(node_t* n, field_t* f)
 
846
{
 
847
        int             i;
 
848
        point   d,d0;
 
849
 
 
850
        if (f->lp) d = cvt2pt(f->lp->dimen);
 
851
        else {
 
852
                d.x = d.y = 0.0;
 
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); }
 
857
                }
 
858
        }
 
859
        f->size = d;
 
860
        return d;
 
861
}
 
862
 
 
863
void
 
864
resize_reclbl(field_t* f, point sz)
 
865
{
 
866
        int                     i,amt;
 
867
        double          inc;
 
868
        point           d,newsz;
 
869
        field_t         *sf;
 
870
 
 
871
        /* adjust field */
 
872
        d.x = sz.x - f->size.x; d.y = sz.y - f->size.y;
 
873
        f->size = sz;
 
874
 
 
875
        /* adjust children */
 
876
        if (f->n_flds) {
 
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++) {
 
880
                        sf = f->fld[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);
 
885
                }
 
886
        }
 
887
}
 
888
 
 
889
void
 
890
pos_reclbl(field_t* f, point ul)
 
891
{
 
892
        int             i;
 
893
 
 
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;
 
900
        }
 
901
}
 
902
 
 
903
/* syntax of labels: foo|bar|baz or foo|(recursive|label)|baz */
 
904
void
 
905
record_init(node_t* n)
 
906
{
 
907
        field_t *info;
 
908
        point   ul,sz;
 
909
    int     len;
 
910
    char*   textbuf;   /* temp buffer for storing labels */
 
911
 
 
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);
 
917
                reclblp = "\\N";
 
918
                info = parse_reclbl(n,NOT(n->graph->u.left_to_right), TRUE, textbuf);
 
919
        }
 
920
    free (textbuf);
 
921
        size_reclbl(n,info);
 
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);
 
926
        pos_reclbl(info,ul);
 
927
        n->u.width = PS2INCH(info->size.x);
 
928
        n->u.height = PS2INCH(info->size.y);
 
929
        n->u.shape_info = (void*) info;
 
930
}
 
931
 
 
932
void
 
933
record_free(node_t* n)
 
934
{
 
935
        field_t* p = n->u.shape_info;
 
936
 
 
937
        free(p);
 
938
}
 
939
 
 
940
field_t *
 
941
map_rec_port(field_t* f, char* str)
 
942
{
 
943
        field_t         *rv;
 
944
        int             sub;
 
945
 
 
946
        if (f->id && (strcmp(f->id,str) == 0)) rv = f;
 
947
        else {
 
948
                rv = NULL;
 
949
                for (sub = 0; sub < f->n_flds; sub++)
 
950
                        if ((rv = map_rec_port(f->fld[sub],str))) break;
 
951
        }
 
952
        return rv;
 
953
}
 
954
 
 
955
port_t
 
956
record_port(node_t* n, char* pname)
 
957
{
 
958
        field_t *f;
 
959
        box             b;
 
960
        port_t  rv;
 
961
 
 
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);
 
965
                return Center;
 
966
        }
 
967
 
 
968
        b = f->b;
 
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;
 
973
        rv.defined = TRUE;
 
974
        return rv;
 
975
}
 
976
 
 
977
int
 
978
record_inside(node_t* n, pointf p, edge_t* e)
 
979
{
 
980
 
 
981
        pointf                  LL,UR;
 
982
        edge_t                  *f;
 
983
        field_t                 *fld0;
 
984
        char                    *pname;
 
985
        static edge_t   *last_e;
 
986
        static node_t   *last_n;
 
987
        static field_t  *fld;
 
988
 
 
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);
 
991
        e = f;
 
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);
 
996
        }
 
997
 
 
998
        if (fld == NULL) {
 
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;
 
1002
        }
 
1003
        else {
 
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;
 
1006
        }
 
1007
        return (BETWEEN(LL.x,p.x,UR.x) && BETWEEN(LL.y,p.y,UR.y));
 
1008
}
 
1009
 
 
1010
static box flip_rec_box(box b, point p)
 
1011
{
 
1012
        box     rv;
 
1013
                /* flip box */
 
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;
 
1016
                /* move box */
 
1017
        rv.LL.x  += p.x; rv.LL.y += p.y;
 
1018
        rv.UR.x  += p.x; rv.UR.y += p.y;
 
1019
        return rv;
 
1020
}
 
1021
 
 
1022
 
 
1023
int
 
1024
record_path(node_t* n, edge_t* e, int pt, box rv[], int* kptr)
 
1025
{
 
1026
        int                     i,side,ls,rs;
 
1027
        point           p;
 
1028
        field_t         *info;
 
1029
 
 
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;
 
1033
 
 
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; }
 
1037
                else
 
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);
 
1043
                        }
 
1044
                        else {
 
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;
 
1048
                        }
 
1049
#if 0
 
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;
 
1053
                        sep = MIN(s0,s1);
 
1054
                        rv[0].LL.x += sep;
 
1055
                        rv[0].UR.x -= sep;
 
1056
#endif
 
1057
                        rv[0].UR.y = n->u.coord.y + n->u.ht/2;
 
1058
                        *kptr = 1;
 
1059
                        break;
 
1060
                }
 
1061
        }
 
1062
        if (pt == 1) side = BOTTOM; else side = TOP;
 
1063
        return side;
 
1064
}
 
1065
 
 
1066
void
 
1067
gen_fields(node_t* n, field_t* f)
 
1068
{
 
1069
        int                     i;
 
1070
        double          cx,cy;
 
1071
        point           A[2];
 
1072
 
 
1073
        if (f->lp) {
 
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);
 
1078
        }
 
1079
 
 
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");
 
1084
        right here */
 
1085
 
 
1086
        for (i = 0; i < f->n_flds; i++) {
 
1087
                if (i > 0) {
 
1088
                        if (f->LR) {
 
1089
                                A[0] = f->fld[i]->b.LL;
 
1090
                                A[1].x = A[0].x;
 
1091
                                A[1].y = f->fld[i]->b.UR.y;
 
1092
                        }
 
1093
                        else {
 
1094
                                A[1] = f->fld[i]->b.UR;
 
1095
                                A[0].x = f->fld[i]->b.LL.x;
 
1096
                                A[0].y = A[1].y;
 
1097
                        }
 
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);
 
1101
                }
 
1102
                gen_fields(n,f->fld[i]);
 
1103
        }
 
1104
}
 
1105
 
 
1106
void
 
1107
record_gencode(node_t* n)
 
1108
{
 
1109
        point   A[4];
 
1110
        int             i,style;
 
1111
        field_t *f;
 
1112
 
 
1113
        f = (field_t*) n->u.shape_info;
 
1114
        A[0] = f->b.LL;
 
1115
        A[2] = f->b.UR;
 
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);
 
1122
        pencolor(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);
 
1127
        gen_fields(n,f);
 
1128
        CodeGen->end_context();
 
1129
        CodeGen->end_node();
 
1130
}
 
1131
 
 
1132
static shape_desc **UserShape;
 
1133
static int      N_UserShape;
 
1134
 
 
1135
shape_desc *
 
1136
find_user_shape(char* name)
 
1137
{
 
1138
        int             i;
 
1139
        if (UserShape) {
 
1140
                for (i = 0; i < N_UserShape; i++) {
 
1141
                        if (streq(UserShape[i]->name,name)) return UserShape[i];
 
1142
                }
 
1143
        }
 
1144
        return NULL;
 
1145
}
 
1146
 
 
1147
shape_desc *
 
1148
user_shape(char* name)
 
1149
{
 
1150
        int                     i;
 
1151
        shape_desc      *p;
 
1152
 
 
1153
        if ((p = find_user_shape(name))) return p;
 
1154
        i = N_UserShape++;
 
1155
        UserShape = ALLOC(N_UserShape,UserShape,shape_desc*);
 
1156
        p = UserShape[i] = NEW(shape_desc);
 
1157
        *p = Shapes[0];
 
1158
        p->name = name;
 
1159
        if (Lib == NULL) fprintf(stderr, "warning: using %s for unknown shape %s\n", Shapes[0].name,p->name);
 
1160
        return p;
 
1161
}
 
1162
 
 
1163
shape_desc *
 
1164
bind_shape(char* name)
 
1165
{
 
1166
        shape_desc      *ptr,*rv= NULL;
 
1167
 
 
1168
        for (ptr = Shapes; ptr->name; ptr++)
 
1169
                if (!strcmp(ptr->name,name)) {rv = ptr; break;}
 
1170
        if (rv == NULL) rv = user_shape(name);
 
1171
        return rv;
 
1172
}
 
1173
 
 
1174
static point
 
1175
interpolate(double t, point p0, point p1)
 
1176
{
 
1177
        point   rv;
 
1178
        rv.x = p0.x + t * (p1.x - p0.x);
 
1179
        rv.y = p0.y + t * (p1.y - p0.y);
 
1180
        return rv;
 
1181
}
 
1182
 
 
1183
void
 
1184
round_corners(node_t *nn, point *A, int n, int style)
 
1185
{
 
1186
        point   *B,C[2],p0,p1;
 
1187
        double  d,dx,dy,t;
 
1188
        int             i,seg,mode;
 
1189
 
 
1190
        if (style & DIAGONALS) mode = DIAGONALS;
 
1191
        else mode = ROUNDED;
 
1192
        B = N_NEW(4*n+4,point);
 
1193
        i = 0;
 
1194
        for (seg = 0; seg < n; seg++) {
 
1195
                p0 = A[seg];
 
1196
                if (seg < n - 1) p1 = A[seg+1];
 
1197
                else p1 = A[0];
 
1198
                dx = p1.x - p0.x;
 
1199
                dy = p1.y - p0.y;
 
1200
                d = sqrt(dx*dx + dy*dy);
 
1201
                /*t = ((mode == ROUNDED)? RBCONST / d : .5);*/
 
1202
                t = RBCONST / d;
 
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);
 
1208
        }
 
1209
        B[i++] = B[0];
 
1210
        B[i++] = B[1];
 
1211
        B[i++] = B[2];
 
1212
 
 
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);
 
1217
                }
 
1218
        }
 
1219
        else {  /* diagonals are weird.  rewrite someday. */
 
1220
                pencolor(nn);
 
1221
                if (style & FILLED) fillcolor(nn); /* emit fill color */
 
1222
                CodeGen->polygon(A,n,style&FILLED);
 
1223
                for (seg = 0; seg < n; seg++) {
 
1224
#ifdef NOTDEF
 
1225
                        C[0] = B[3 * seg]; C[1] = B[3 * seg + 3];
 
1226
                        CodeGen->polyline(C,2);
 
1227
#endif
 
1228
                        C[0] = B[3 * seg + 2]; C[1] = B[3 * seg + 4];
 
1229
                        CodeGen->polyline(C,2);
 
1230
                }
 
1231
        }
 
1232
        free(B);
 
1233
}
 
1234
 
 
1235
void
 
1236
draw_user_shape(node_t *n, point *A, int sides, int filled)
 
1237
{
 
1238
}
 
1239
 
 
1240
int
 
1241
epsf_inside(node_t* n, pointf p, edge_t* e)
 
1242
{
 
1243
        pointf  P;
 
1244
        double  x2;
 
1245
 
 
1246
        P = (n->graph->u.left_to_right? flip_ptf(p) : p);
 
1247
        x2 = n->u.ht / 2;
 
1248
        return ((P.y >= -x2) && (P.y <= x2) && (P.x >= -n->u.lw) && (P.x <= n->u.rw));
 
1249
}