/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ #pragma prototyped #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "render.h" #include "utils.h" #define FILLED (1 << 0) #define ROUNDED (1 << 1) #define DIAGONALS (1 << 2) #define AUXLABELS (1 << 3) #define INVISIBLE (1 << 4) #define RBCONST 12 #define RBCURVE .5 #ifndef HAVE_SINCOS void sincos(x,s,c) double x,*s,*c; { *s = sin(x); *c = cos(x); } #else extern void sincos(double x, double *s, double *c); #endif static port_t Center = { {0,0}, -1, 0, 0, 0}; #define ATTR_SET(a,n) ((a) && (*(agxget(n,a->index)) != '\0')) #define DEF_POINT 0.05 /* extra null character needed to avoid style emitter from thinking * there are arguments. */ static char* point_style[3] = {"invis\0","filled\0",0}; static shape_desc* point_desc; /* forward declarations of functions used in shapes tables */ /* static void poly_init(node_t *); */ /* static void point_init(node_t *); */ /* static void record_init(node_t *); */ static void poly_free(node_t *); static port_t poly_port(node_t*, char *); static int poly_inside(node_t*, pointf, edge_t *); static void poly_gencode(node_t*); #ifdef NOT_READY_YET static void poly_shape_init(node_t *); static void shape_free(node_t *); static port_t shape_port(node_t*, char *); static int shape_inside(node_t*, pointf, edge_t *); static void shape_gencode(node_t*); #endif static void record_free(node_t *); static port_t record_port(node_t*, char *); static int record_inside(node_t*, pointf, edge_t *); static int record_path(node_t* n, edge_t* e, int pt, box rv[], int* kptr); static void record_gencode(node_t*); #ifdef NOT_READY_YET extern void tbl_init(node_t *); extern void tbl_free(node_t *); extern port_t tbl_port(node_t*, char *); extern int tbl_inside(node_t*, pointf, edge_t *); extern int tbl_path(node_t* n, edge_t* e, int pt, box rv[], int* kptr); extern void tbl_gencode(node_t*); #endif static int epsf_inside(node_t*, pointf, edge_t *); /* polygon descriptions. "polygon" with 0 sides takes all user control */ /* regul perip sides orien disto skew */ static polygon_t p_polygon = { FALSE, 1, 0, 0., 0., 0. }; /* builtin polygon descriptions */ static polygon_t p_ellipse = { FALSE, 1, 1, 0., 0., 0. }; static polygon_t p_circle = { TRUE, 1, 1, 0., 0., 0. }; static polygon_t p_egg = { FALSE, 1, 1, 0., -.3, 0. }; static polygon_t p_triangle = { FALSE, 1, 3, 0., 0., 0. }; static polygon_t p_box = { FALSE, 1, 4, 0., 0., 0. }; static polygon_t p_plaintext = { FALSE, 0, 4, 0., 0., 0. }; static polygon_t p_diamond = { FALSE, 1, 4, 45., 0., 0. }; static polygon_t p_trapezium = { FALSE, 1, 4, 0., -.4, 0. }; static polygon_t p_parallelogram = { FALSE, 1, 4, 0., 0., .6 }; static polygon_t p_house = { FALSE, 1, 5, 0., -.64, 0. }; static polygon_t p_pentagon = { FALSE, 1, 5, 0., 0., 0. }; static polygon_t p_hexagon = { FALSE, 1, 6, 0., 0., 0. }; static polygon_t p_septagon = { FALSE, 1, 7, 0., 0., 0. }; static polygon_t p_octagon = { FALSE, 1, 8, 0., 0., 0. }; /* redundant and undocumented builtin polygons */ static polygon_t p_doublecircle = { TRUE, 2, 1, 0., 0., 0. }; static polygon_t p_invtriangle = { FALSE, 1, 3, 180., 0., 0. }; static polygon_t p_invtrapezium = { FALSE, 1, 4, 180., -.4, 0. }; static polygon_t p_invhouse = { FALSE, 1, 5, 180., -.64, 0. }; static polygon_t p_doubleoctagon = { FALSE, 2, 8, 0., 0., 0. }; static polygon_t p_tripleoctagon = { FALSE, 3, 8, 0., 0., 0. }; static polygon_t p_Mdiamond = { FALSE, 1, 4, 45., 0., 0. ,DIAGONALS|AUXLABELS}; static polygon_t p_Msquare = { TRUE, 1, 4, 0., 0., 0. ,DIAGONALS}; static polygon_t p_Mcircle = { TRUE, 1, 1, 0., 0., 0.,DIAGONALS|AUXLABELS}; /* * every shape has these functions: * * void SHAPE_init(node_t *n) * initialize the shape (usually at least its size). * port_t SHAPE_port(node_t *n, char *portname) * return the aiming point and slope (if constrained) * of a port. * int SHAPE_inside(node_t *n, pointf p, edge_t *e); * test if point is inside the node shape which is * assumed convex. * the point is relative to the node center. the edge * is passed in case the port affects spline clipping. * void SHAPE_code(node_t *n) * generate graphics code for a node. * int SHAPE_path(node_t *n, edge_t *e, int pt, box path[], int *nbox) * create a path for the port of e that touches n, * return side * * some shapes, polygons in particular, use additional shape control data * * */ /* this needs an overhaul */ static shape_desc Shapes[] = { /* first entry is default for no such shape */ {"box" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_box }, {"polygon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_polygon }, {"ellipse" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_ellipse }, {"circle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_circle }, {"point" ,point_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_circle }, {"egg" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_egg }, {"triangle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_triangle }, {"plaintext" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_plaintext }, {"diamond" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_diamond }, {"trapezium" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_trapezium }, {"parallelogram",poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_parallelogram }, {"house" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_house }, {"pentagon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_pentagon }, {"hexagon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_hexagon }, {"septagon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_septagon }, {"octagon" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_octagon }, /* redundant and undocumented builtin polygons */ {"rect" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_box }, {"rectangle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_box }, {"doublecircle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_doublecircle }, {"doubleoctagon",poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_doubleoctagon }, {"tripleoctagon",poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_tripleoctagon }, {"invtriangle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_invtriangle }, {"invtrapezium" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_invtrapezium }, {"invhouse" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_invhouse }, {"Mdiamond" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_Mdiamond }, {"Msquare" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_Msquare }, {"Mcircle" ,poly_init,poly_free,poly_port,poly_inside,NULL,poly_gencode,&p_Mcircle }, /* *** shapes other than polygons *** */ {"record" ,record_init,record_free,record_port,record_inside,record_path,record_gencode,NULL}, {"Mrecord" ,record_init,record_free,record_port,record_inside,record_path,record_gencode,NULL}, {"epsf" ,epsf_init,epsf_free,poly_port,epsf_inside,NULL,epsf_gencode,NULL}, #ifdef NOT_READY_YET {"table" ,tbl_init,tbl_free,tbl_port,tbl_inside,tbl_path,tbl_gencode,NULL}, #endif {NULL ,NULL,NULL,NULL,NULL,NULL,NULL,NULL} }; #if 0 /* not used */ static point flip_pt(point p) { int t = p.x; p.x = -p.y; p.y = t; return p; } #endif static point invflip_pt(point p) { int t = p.x; p.x = p.y; p.y = -t; return p; } static pointf flip_ptf(pointf p) { double t = p.x; p.x = -p.y; p.y = t; return p; } static void storeline(textlabel_t *lp, char *line, char terminator,graph_t *g) { double width = 0.0; int iwidth = 0; lp->line = ALLOC(lp->nlines+2,lp->line,textline_t); lp->line[lp->nlines].str = line; if (line) { if (CodeGen && CodeGen->textsize) iwidth = (CodeGen->textsize(line, lp->fontname,lp->fontsize).x); if (!iwidth) iwidth = estimate_textsize(line, lp->fontname,lp->fontsize).x; width = (double)iwidth; } lp->line[lp->nlines].width = width; lp->line[lp->nlines].just = terminator; lp->nlines++; if (width > 0) { width += lp->fontsize; /* margins */ width = PS2INCH(width); if (lp->dimen.x < width) lp->dimen.x = width; } /* recalculate total height, including a small extra margin * for top and bottom */ lp->dimen.y = PS2INCH(lp->nlines*(int)(lp->fontsize*LINESPACING) + 4); } /* compiles into a label and returns its bounding box size. */ pointf label_size(char *str, textlabel_t *lp, graph_t *g) { char c,*p,*line,*lineptr; unsigned char byte = 0x00; if (*str == '\0') return lp->dimen; line = lineptr = NULL; p = str; line = lineptr = N_GNEW(strlen(p) + 1,char); *line = 0; while ((c = *p++)) { byte = (unsigned int )c; if (c & ~0x7f) GD_has_Latin1char(g) = TRUE; //Fix some Double Character error for Big5 (Start) if (0xA1 <= byte && byte <= 0xFE) { *lineptr++ = c; c = *p++; *lineptr++ = c; //Fix some Double Character error for Big5 (End) }else{ if (c == '\\') { switch (*p) { case 'n': case 'l': case 'r': *lineptr++ = '\0'; storeline(lp,line,*p,g); line = lineptr; break; default: *lineptr++ = *p; } if (*p) p++; /* tcldot can enter real linend characters */ } else if (c == '\n') { *lineptr++ = '\0'; storeline(lp,line,'n',g); line = lineptr; }else{ *lineptr++ = c; } } } if (line != lineptr) { *lineptr++ = '\0'; storeline(lp,line,'n',g); } return lp->dimen; } static void unrecognized(node_t* n, char* p) { agerr(AGWARN, "node %s, port %s unrecognized\n",n->name,p); } #define GAP (PS2INCH(4.)) static double quant(double val, double q) { int i; i = val / q; if (i * q + .00001 < val) i++; return i * q; } static int same_side(pointf p0, pointf p1, pointf L0, pointf L1) { int s0,s1; double a,b,c; /* a x + b y = c */ a = -(L1.y - L0.y); b = (L1.x - L0.x); c = a * L0.x + b * L0.y; s0 = (a*p0.x + b*p0.y - c >= 0); s1 = (a*p1.x + b*p1.y - c >= 0); return (s0 == s1); } void pencolor(node_t *n) { char *color; color = late_nnstring(n,N_color,""); if (color[0]) CodeGen->set_pencolor(color); } static char* findFill(node_t *n) { char *color; color = late_nnstring(n,N_fillcolor,""); if (! color[0]) { /* for backward compatibilty, default fill is same as pen */ color = late_nnstring(n,N_color,""); if (! color[0]) { if (ND_shape(n) == point_desc) { color = "black"; } else { color = (Output_lang == MIF ? "black" : DEFAULT_FILL); } } } return color; } void fillcolor(node_t *n) { CodeGen->set_fillcolor(findFill(n)); } static char** checkStyle (node_t* n, int* flagp) { char* style; char** pstyle = 0; int istyle = 0; polygon_t* poly; style = late_nnstring(n,N_style,""); if (style[0]) { int i; pstyle = parse_style(style); for (i = 0; pstyle[i]; i++) { if (strcmp(pstyle[i],"filled") == 0) {istyle |= FILLED;} else if (strcmp(pstyle[i],"rounded") == 0) {istyle |= ROUNDED;} else if (strcmp(pstyle[i],"diagonals") == 0) {istyle |= DIAGONALS;} else if (strcmp(pstyle[i],"invis") == 0) {istyle |= INVISIBLE;} } } if ((poly = ND_shape(n)->polygon)) istyle |= poly->option; *flagp = istyle; return pstyle; } int stylenode(node_t* n) { char** pstyle; int istyle; if ((pstyle = checkStyle (n, &istyle))) CodeGen->set_style(pstyle); return istyle; } static void hack1(node_t* n, char* str, int k) { point p; double fontsize; textline_t fake; fontsize = ND_label(n)->fontsize*.8; /* magic? */ p.x = ND_coord_i(n).x - (strlen(str) * ND_label(n)->fontsize) / 2; p.y = ND_coord_i(n).y + (k * (ND_ht_i(n) - ND_label(n)->fontsize - 2)) / 2; CodeGen->begin_context(); CodeGen->set_font(ND_label(n)->fontname,fontsize); fake.str = str; fake.width = strlen(str) * fontsize; /* ugh */ fake.just = 0; /* does this field do anything? SCN */ CodeGen->textline(p,&fake); CodeGen->end_context(); } static void Mlabel_hack(node_t* n) { char *str; if ((str = agget(n,"toplabel"))) hack1(n,str,1); if ((str = agget(n,"bottomlabel"))) hack1(n,str,-1); } static void Mcircle_hack(node_t* n) { double x,y; point A[2],p; y = .7500; x = .6614; /* x^2 + y^2 = 1.0 */ p.y = y * ND_ht_i(n) / 2.0; p.x = ND_rw_i(n) * x; /* assume node is symmetric */ A[0] = add_points(p,ND_coord_i(n)); A[1].y = A[0].y; A[1].x = A[0].x - 2*p.x; CodeGen->polyline(A,2); A[0].y -= 2*p.y; A[1].y = A[0].y; CodeGen->polyline(A,2); } static point interpolate(double t, point p0, point p1) { point rv; rv.x = p0.x + t * (p1.x - p0.x); rv.y = p0.y + t * (p1.y - p0.y); return rv; } static void round_corners(node_t *nn, point *A, int n, int style) { point *B,C[2],p0,p1; double d,dx,dy,t; int i,seg,mode; if (style & DIAGONALS) mode = DIAGONALS; else mode = ROUNDED; B = N_NEW(4*n+4,point); i = 0; for (seg = 0; seg < n; seg++) { p0 = A[seg]; if (seg < n - 1) p1 = A[seg+1]; else p1 = A[0]; dx = p1.x - p0.x; dy = p1.y - p0.y; d = sqrt(dx*dx + dy*dy); /*t = ((mode == ROUNDED)? RBCONST / d : .5);*/ t = RBCONST / d; if (mode != ROUNDED) B[i++] = p0; if (mode == ROUNDED) B[i++] = interpolate(RBCURVE*t,p0,p1); B[i++] = interpolate(t,p0,p1); B[i++] = interpolate(1.0 - t,p0,p1); if (mode == ROUNDED) B[i++] = interpolate(1.0 - RBCURVE*t,p0,p1); } B[i++] = B[0]; B[i++] = B[1]; B[i++] = B[2]; if (mode == ROUNDED) { for (seg = 0; seg < n; seg++) { CodeGen->polyline(B + 4*seg+1,2); CodeGen->beziercurve(B + 4*seg+2,4,FALSE,FALSE); } } else { /* diagonals are weird. rewrite someday. */ pencolor(nn); if (style & FILLED) fillcolor(nn); /* emit fill color */ CodeGen->polygon(A,n,style&FILLED); for (seg = 0; seg < n; seg++) { #ifdef NOTDEF C[0] = B[3 * seg]; C[1] = B[3 * seg + 3]; CodeGen->polyline(C,2); #endif C[0] = B[3 * seg + 2]; C[1] = B[3 * seg + 4]; CodeGen->polyline(C,2); } } free(B); } /*=============================poly start=========================*/ void poly_init(node_t* n) { pointf dimen; pointf P,Q,R; pointf *vertices; double temp,alpha,beta,gamma,delta,xb,yb; double orientation,distortion,skew; double sectorangle, sidelength, skewdist, gdistortion, gskew; double angle, sinx, cosx, xmax, ymax, scalex, scaley; int regular,peripheries,sides; int i,j,outp; polygon_t *poly=NEW(polygon_t); regular = ND_shape(n)->polygon->regular; peripheries = ND_shape(n)->polygon->peripheries; sides = ND_shape(n)->polygon->sides; orientation = ND_shape(n)->polygon->orientation; skew = ND_shape(n)->polygon->skew; distortion = ND_shape(n)->polygon->distortion; regular |= mapbool(agget(n,"regular")); peripheries = late_int(n,N_peripheries,peripheries,0); orientation += late_double(n,N_orientation,0.0,-360.0); if (sides==0) { /* not for builtins */ skew = late_double(n,N_skew,0.0,-100.0); sides = late_int(n,N_sides,4,0); distortion = late_double(n,N_distortion,0.0,-100.0); } /* get label dimensions */ dimen = ND_label(n)->dimen; if (mapbool(late_string(n,N_fixed,"false"))) { if ((ND_width(n) < dimen.x) || (ND_height(n) < dimen.y)) agerr(AGWARN, "node '%s' size too small for label\n", n->name); dimen.x = dimen.y = 0; } else { if (ND_shape(n)->usershape && CodeGen && CodeGen->usershapesize) { point imagesize; char* sfile = agget(n,"shapefile"); imagesize = CodeGen->usershapesize(n,sfile); if ((imagesize.x == 0) && (imagesize.y == 0)) { agerr(AGERR, "No or improper shapefile=\"%s\" for user-defined shape=\"%s\" on node \"%s\"\n", (sfile ? sfile : ""), agget(n,"shape"), n->name); } dimen.x = MAX(dimen.x,PS2INCH(imagesize.x)); dimen.y = MAX(dimen.y,PS2INCH(imagesize.y)); } } /* quantization */ if ((temp = GD_drawing(n->graph)->quantum) > 0.0) { dimen.x = quant(dimen.x,temp); dimen.y = quant(dimen.y,temp); } /* make square if necessary */ if (regular) { /* make x and y dimensions equal */ ND_width(n) = ND_height(n) = MIN(ND_width(n),ND_height(n)); xb = yb = MAX(dimen.x,dimen.y); } else { xb = dimen.x; yb = dimen.y; } /* I don't know how to distort or skew ellipses in postscript */ /* Convert request to a polygon with a large number of sides */ if ((sides<=2) && ((distortion!=0.) || (skew!=0.))) { sides = 120; } /* adjust bounding box so that label fits in inner ellipse */ /* this will change the symmetry of the bounding box */ /* adjust for inner to outer diameter of polygon */ if (sides>2) { /* except ellipses */ temp = cos(PI/sides); xb /= temp; yb /= temp; } if ( (sides!=4) || ((ROUND(orientation)%90)!=0) || (distortion!=0.) || (skew!=0.) ) { if (yb>xb) temp = xb * (sqrt(2.) - 1.); else temp = yb * (sqrt(2.) - 1.); xb += temp; yb += temp; } xb=MAX(ND_width(n),xb); yb=MAX(ND_height(n),yb); outp=peripheries; if (peripheries<1) outp=1; if (sides<3) { /* ellipses */ sides=1; vertices=N_NEW(outp,pointf); P.x=xb/2.; P.y=yb/2.; vertices[0] = P; if (peripheries>1) { for (j=1; j1) { Q = vertices[(sides-1)]; R = vertices[0]; beta = atan2(R.y-Q.y,R.x-Q.x); for (i=0; iregular = regular; poly->peripheries = peripheries; poly->sides = sides; poly->orientation = orientation; poly->skew = skew; poly->distortion = distortion; poly->vertices = vertices; ND_width(n) = xb; ND_height(n) = yb; ND_shape_info(n) = (void*) poly; } static void poly_free(node_t *n) { polygon_t* p = ND_shape_info(n); if (p) { free(p->vertices); free(p); } } static int poly_inside(node_t* n, pointf p, edge_t* e) { static polygon_t *poly; static int last,outp,sides; static node_t *lastn; static pointf O; static pointf *vertex; static double xsize,ysize,scalex,scaley,box_URx,box_URy; int i,i1,j,s; pointf P,Q,R; e = e; P = (GD_left_to_right(n->graph)? flip_ptf(p) : p); if (n != lastn) { poly = (polygon_t*) ND_shape_info(n); vertex = poly->vertices; sides = poly->sides; lastn = n; /* get point and node size adjusted for rankdir=LR */ if (GD_left_to_right(n->graph)) { ysize = ND_lw_i(n) + ND_rw_i(n); xsize = ND_ht_i(n); } else { xsize = ND_lw_i(n) + ND_rw_i(n); ysize = ND_ht_i(n); } /* scale */ if (xsize == 0.0) xsize = 1.0; if (ysize == 0.0) ysize = 1.0; scalex = ND_width(n)/xsize; scaley = ND_height(n)/ysize; box_URx = ND_width(n)/2.0; box_URy = ND_height(n)/2.0; /* index to outer-periphery */ outp=(poly->peripheries-1)*sides; if (outp<0) outp=0; } /* scale */ P.x *= scalex; P.y *= scaley; /* inside bounding box? */ if ((fabs(P.x)>box_URx) || (fabs(P.y)>box_URy)) return FALSE; /* ellipses */ if (sides<=2) return (hypot(P.x/box_URx,P.y/box_URy)<1.); /* use fast test in case we are converging on a segment */ i = last % sides; /*in case last left over from larger polygon*/ i1 = (i + 1) % sides; Q = vertex[i+outp]; R = vertex[i1+outp]; if ( !(same_side(P,O,Q,R))) return FALSE; if ( (s=same_side(P,Q,R,O)) && (same_side(P,R,O,Q))) return TRUE; for (j = 1; j < sides; j++) { if (s) { i = i1; i1 = (i + 1) % sides; } else { i1 = i; i = (i + sides - 1) % sides; } if ( !(same_side(P,O,vertex[i+outp],vertex[i1+outp]))) { last = i; return FALSE; } } last = i; /* in case next edge is to same side */ return TRUE; } static port_t poly_port(node_t* n, char* pname) { static char *points_of_compass[] = {"n","ne","e","se","s","sw","w","nw",NULL}; static struct {signed char x,y;} a[] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; int i,ht,wd; port_t rv; char *p; if (*pname) pname++; /* skip over delim */ for (i = 0; (p = points_of_compass[i]); i++) if (streq(p,pname)) break; if (p == NULL) { if (pname[0]) unrecognized(n,pname); rv = Center; } else { ht = ND_ht_i(n) / 2; wd = ND_lw_i(n); rv.p.x = a[i].x * wd; rv.p.y = a[i].y * ht; rv.order = (MC_SCALE * (ND_lw_i(n) + rv.p.x)) / (ND_lw_i(n) + ND_rw_i(n)); rv.constrained = FALSE; rv.defined = TRUE; } return rv; } /* generic polygon gencode routine */ static void poly_gencode(node_t* n) { polygon_t *poly; double xsize, ysize; int i,j,peripheries,sides,style; pointf P,*vertices; static point *A; static int A_size; int filled; poly = (polygon_t*) ND_shape_info(n); vertices = poly->vertices; sides = poly->sides; peripheries = poly->peripheries; if (A_size < sides) {A_size = sides + 5; A = ALLOC(A_size,A,point);} CodeGen->begin_node(n); CodeGen->begin_context(); /* prescale by 16.0 to help rounding trick below */ xsize = ((ND_lw_i(n) + ND_rw_i(n)) / ND_width(n)) * 16.0; ysize = ((ND_ht_i(n)) / ND_height(n)) * 16.0; /* this is bad, but it's because of how the VRML driver works */ #ifdef HAVE_LIBPNG if ((CodeGen == &VRML_CodeGen) && (peripheries == 0)) { peripheries = 1; } #endif if (ND_shape(n) == point_desc) { checkStyle (n, &style); if (style & INVISIBLE) CodeGen->set_style(point_style); else CodeGen->set_style(&point_style[1]); style = FILLED; } else { style = stylenode(n); } if (style & FILLED) { fillcolor(n); /* emit fill color */ filled = 1; } else { filled = 0; } /* if no boundary but filled, set boundary color to fill color */ if ((peripheries == 0) && filled) { char* color; peripheries = 1; color = findFill (n); if (color[0]) CodeGen->set_pencolor(color); } else pencolor(n); /* emit pen color */ if (ND_shape(n)->usershape && CodeGen->user_shape) { for (i = 0; i < sides; i++) { P = vertices[i]; /* simple rounding produces random results around .5 * this trick should clip off the random part. * (note xsize/ysize prescaled by 16.0 above) */ A[i].x = ROUND(P.x * xsize) / 16; A[i].y = ROUND(P.y * ysize) / 16; if (sides > 2) { A[i].x += ND_coord_i(n).x; A[i].y += ND_coord_i(n).y; } } CodeGen->user_shape(ND_shape(n)->name,A,sides,filled); filled = 0; } for (j = 0; j < peripheries; j++) { for (i = 0; i < sides; i++) { P = vertices[i+j*sides]; /* simple rounding produces random results around .5 * this trick should clip off the random part. * (note xsize/ysize prescaled by 16.0 above) */ A[i].x = ROUND(P.x * xsize) / 16; A[i].y = ROUND(P.y * ysize) / 16; if (sides > 2) { A[i].x += ND_coord_i(n).x; A[i].y += ND_coord_i(n).y; } } if (sides <= 2) { CodeGen->ellipse(ND_coord_i(n),A[0].x,A[0].y,filled); if (style & DIAGONALS) { Mcircle_hack(n); } } else if (style & (ROUNDED | DIAGONALS)) { round_corners(n,A,sides,style); } else { CodeGen->polygon(A,sides,filled); } /* fill innermost periphery only */ filled = 0; } if (style & AUXLABELS) Mlabel_hack(n); ND_label(n)->p = ND_coord_i(n); emit_label(ND_label(n),n->graph); CodeGen->end_context(); CodeGen->end_node(); } /*=======================end poly======================================*/ /*=======================start shape======================================*/ #ifdef NOT_READY_YET static void poly_shape_init(node_t* n) { pointf dimen; pointf P,Q,R; pointf *vertices; double temp,alpha,beta,gamma,delta,xb,yb; double orientation,distortion,skew; double sectorangle, sidelength, skewdist, gdistortion, gskew; double angle, sinx, cosx, xmax, ymax, scalex, scaley; int regular,peripheries,sides; int i,j,outp; polygon_t *poly=NEW(polygon_t); regular = ND_shape(n)->polygon->regular; peripheries = ND_shape(n)->polygon->peripheries; sides = ND_shape(n)->polygon->sides; orientation = ND_shape(n)->polygon->orientation; skew = ND_shape(n)->polygon->skew; distortion = ND_shape(n)->polygon->distortion; regular |= mapbool(agget(n,"regular")); peripheries = late_int(n,N_peripheries,peripheries,0); orientation += late_double(n,N_orientation,0.0,-360.0); if (sides==0) { /* not for builtins */ skew = late_double(n,N_skew,0.0,-100.0); sides = late_int(n,N_sides,4,0); distortion = late_double(n,N_distortion,0.0,-100.0); } /* get label dimensions */ dimen = ND_label(n)->dimen; if (mapbool(late_string(n,N_fixed,"false"))) { if ((ND_width(n) < dimen.x) || (ND_height(n) < dimen.y)) agerr(AGWARN, "node '%s' size too small for label\n", n->name); dimen.x = dimen.y = 0; } else { if (ND_shape(n)->usershape && CodeGen && CodeGen->usershapesize) { point imagesize; char* sfile = agget(n,"shapefile"); imagesize = CodeGen->usershapesize(n,sfile); if ((imagesize.x == 0) && (imagesize.y == 0)) { agerr(AGERR, "No or improper shapefile=\"%s\" for user-defined shape=\"%s\" on node \"%s\"\n", (sfile ? sfile : ""), agget(n,"shape"), n->name); } dimen.x = MAX(dimen.x,PS2INCH(imagesize.x)); dimen.y = MAX(dimen.y,PS2INCH(imagesize.y)); } } /* quantization */ if ((temp = GD_drawing(n->graph)->quantum) > 0.0) { dimen.x = quant(dimen.x,temp); dimen.y = quant(dimen.y,temp); } /* make square if necessary */ if (regular) { /* make x and y dimensions equal */ ND_width(n) = ND_height(n) = MIN(ND_width(n),ND_height(n)); xb = yb = MAX(dimen.x,dimen.y); } else { xb = dimen.x; yb = dimen.y; } /* I don't know how to distort or skew ellipses in postscript */ /* Convert request to a polygon with a large number of sides */ if ((sides<=2) && ((distortion!=0.) || (skew!=0.))) { sides = 120; } /* adjust bounding box so that label fits in inner ellipse */ /* this will change the symmetry of the bounding box */ /* adjust for inner to outer diameter of polygon */ if (sides>2) { /* except ellipses */ temp = cos(PI/sides); xb /= temp; yb /= temp; } if ( (sides!=4) || ((ROUND(orientation)%90)!=0) || (distortion!=0.) || (skew!=0.) ) { if (yb>xb) temp = xb * (sqrt(2.) - 1.); else temp = yb * (sqrt(2.) - 1.); xb += temp; yb += temp; } xb=MAX(ND_width(n),xb); yb=MAX(ND_height(n),yb); outp=peripheries; if (peripheries<1) outp=1; if (sides<3) { /* ellipses */ sides=1; vertices=N_NEW(outp,pointf); P.x=xb/2.; P.y=yb/2.; vertices[0] = P; if (peripheries>1) { for (j=1; j1) { Q = vertices[(sides-1)]; R = vertices[0]; beta = atan2(R.y-Q.y,R.x-Q.x); for (i=0; iregular = regular; poly->peripheries = peripheries; poly->sides = sides; poly->orientation = orientation; poly->skew = skew; poly->distortion = distortion; poly->vertices = vertices; ND_width(n) = xb; ND_height(n) = yb; ND_shape_info(n) = (void*) poly; } static void shape_free(node_t *n) { polygon_t* p = ND_shape_info(n); if (p) { free(p->vertices); free(p); } } static int shape_inside(node_t* n, pointf p, edge_t* e) { static polygon_t *poly; static int last,outp,sides; static node_t *lastn; static pointf O; static pointf *vertex; static double xsize,ysize,scalex,scaley,box_URx,box_URy; int i,i1,j,s; pointf P,Q,R; e = e; P = (GD_left_to_right(n->graph)? flip_ptf(p) : p); if (n != lastn) { poly = (polygon_t*) ND_shape_info(n); vertex = poly->vertices; sides = poly->sides; lastn = n; /* get point and node size adjusted for rankdir=LR */ if (GD_left_to_right(n->graph)) { ysize = ND_lw_i(n) + ND_rw_i(n); xsize = ND_ht_i(n); } else { xsize = ND_lw_i(n) + ND_rw_i(n); ysize = ND_ht_i(n); } /* scale */ if (xsize == 0.0) xsize = 1.0; if (ysize == 0.0) ysize = 1.0; scalex = ND_width(n)/xsize; scaley = ND_height(n)/ysize; box_URx = ND_width(n)/2.0; box_URy = ND_height(n)/2.0; /* index to outer-periphery */ outp=(poly->peripheries-1)*sides; if (outp<0) outp=0; } /* scale */ P.x *= scalex; P.y *= scaley; /* inside bounding box? */ if ((fabs(P.x)>box_URx) || (fabs(P.y)>box_URy)) return FALSE; /* ellipses */ if (sides<=2) return (hypot(P.x/box_URx,P.y/box_URy)<1.); /* use fast test in case we are converging on a segment */ i = last % sides; /*in case last left over from larger polygon*/ i1 = (i + 1) % sides; Q = vertex[i+outp]; R = vertex[i1+outp]; if ( !(same_side(P,O,Q,R))) return FALSE; if ( (s=same_side(P,Q,R,O)) && (same_side(P,R,O,Q))) return TRUE; for (j = 1; j < sides; j++) { if (s) { i = i1; i1 = (i + 1) % sides; } else { i1 = i; i = (i + sides - 1) % sides; } if ( !(same_side(P,O,vertex[i+outp],vertex[i1+outp]))) { last = i; return FALSE; } } last = i; /* in case next edge is to same side */ return TRUE; } static port_t shape_port(node_t* n, char* pname) { static char *points_of_compass[] = {"n","ne","e","se","s","sw","w","nw",NULL}; static struct {signed char x,y;} a[] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; int i,ht,wd; port_t rv; char *p; if (*pname) pname++; /* skip over delim */ for (i = 0; (p = points_of_compass[i]); i++) if (streq(p,pname)) break; if (p == NULL) { if (pname[0]) unrecognized(n,pname); rv = Center; } else { ht = ND_ht_i(n) / 2; wd = ND_lw_i(n); rv.p.x = a[i].x * wd; rv.p.y = a[i].y * ht; rv.order = (MC_SCALE * (ND_lw_i(n) + rv.p.x)) / (ND_lw_i(n) + ND_rw_i(n)); rv.constrained = FALSE; rv.defined = TRUE; } return rv; } /* generic shape gencode routine */ static void shape_gencode(node_t* n) { polygon_t *poly; double xsize, ysize; int i,j,peripheries,sides,style; pointf P,*vertices; static point *A; static int A_size; int filled; poly = (polygon_t*) ND_shape_info(n); vertices = poly->vertices; sides = poly->sides; peripheries = poly->peripheries; if (A_size < sides) {A_size = sides + 5; A = ALLOC(A_size,A,point);} CodeGen->begin_node(n); CodeGen->begin_context(); /* prescale by 16.0 to help rounding trick below */ xsize = ((ND_lw_i(n) + ND_rw_i(n)) / ND_width(n)) * 16.0; ysize = ((ND_ht_i(n)) / ND_height(n)) * 16.0; /* this is bad, but it's because of how the VRML driver works */ #ifdef HAVE_LIBPNG if ((CodeGen == &VRML_CodeGen) && (peripheries == 0)) { peripheries = 1; } #endif if (ND_shape(n) == point_desc) { checkStyle (n, &style); if (style & INVISIBLE) CodeGen->set_style(point_style); else CodeGen->set_style(&point_style[1]); style = FILLED; } else style = stylenode(n); pencolor(n); /* emit pen color */ if (style & FILLED) fillcolor(n); /* emit fill color */ for (j = 0; j < peripheries; j++) { for (i = 0; i < sides; i++) { P = vertices[i+j*sides]; /* simple rounding produces random results around .5 * this trick should clip off the random part. * (note xsize/ysize prescaled by 16.0 above) */ A[i].x = ROUND(P.x * xsize) / 16; A[i].y = ROUND(P.y * ysize) / 16; if (sides > 2) {A[i].x += ND_coord_i(n).x; A[i].y += ND_coord_i(n).y;} } if (!j && (style & FILLED)) { /* fill innermost periphery only */ filled = 1; } else { filled = 0; } if (ND_shape(n)->usershape && CodeGen->user_shape) { CodeGen->user_shape(ND_shape(n)->name,A,sides,filled); } else if (sides <= 2) { CodeGen->ellipse(ND_coord_i(n),A[0].x,A[0].y,filled); if (style & DIAGONALS) { Mcircle_hack(n); } } else if (style & (ROUNDED | DIAGONALS)) { round_corners(n,A,sides,style); } else { CodeGen->polygon(A,sides,filled); } } if (style & AUXLABELS) Mlabel_hack(n); ND_label(n)->p = ND_coord_i(n); emit_label(ND_label(n),n->graph); CodeGen->end_context(); CodeGen->end_node(); } #endif /*=======================end shape======================================*/ /*===============================point start========================*/ /* point_init: * shorthand for shape=circle, style=filled, width=0.05, label="" */ void point_init(node_t* n) { textlabel_t* p; if (!point_desc) { shape_desc *ptr; for (ptr = Shapes; ptr->name; ptr++) if (!strcmp(ptr->name,"point")) {point_desc = ptr; break;} assert(point_desc); } /* adjust label to "" */ p = ND_label(n); if (p->nlines != 0) free(p->line[0].str); free(p->line); free(p->text); p->nlines = 0; p->line = 0; p->text = strdup(""); p->dimen.x = 0; p->dimen.y = 0; /* set width and height, and make them equal * if user has set weight or height, use it. * if both are set, use smallest. * if neither, use default */ if (ATTR_SET(N_width,n)) { if (ATTR_SET(N_height,n)) { ND_width(n) = ND_height(n) = MIN(ND_width(n),ND_height(n)); } else ND_height(n) = ND_width(n); } else if (ATTR_SET(N_height,n)) { ND_width(n) = ND_height(n); } else ND_width(n) = ND_height(n) = DEF_POINT; poly_init (n); } /* the "record" shape is a rudimentary table formatter */ #define HASTEXT 1 #define HASPORT 2 #define HASTABLE 4 #define INTEXT 8 #define INPORT 16 #define ISCTRL(c) ((c) == '{' || (c) == '}' || (c) == '|' || (c) == '<' || (c) == '>') static char *reclblp; static field_t * parse_reclbl(node_t *n, int LR, int flag, char* text) { field_t *fp, *rv = NEW(field_t); char *tsp, *psp, *hstsp, *hspsp, *sp; char port[SMALLBUF]; int maxf, cnt, mode, wflag, ishardspace, fi; fp = NULL; for (maxf = 1, cnt = 0, sp = reclblp; *sp; sp++) { if (*sp == '\\') { sp++; if (*sp && (*sp == '{' || *sp == '}' || *sp == '|')) continue; } if (*sp == '{') cnt++; else if (*sp == '}') cnt--; else if (*sp == '|' && cnt == 0) maxf++; if (cnt < 0) break; } /*maxf = strccnt(reclblp, '|') + 1;*/ rv->fld = N_NEW (maxf, field_t*); rv->LR = LR; mode = 0; fi = 0; hstsp = tsp = text, hspsp = psp = &port[0]; wflag = TRUE; ishardspace = FALSE; while (wflag) { switch (*reclblp) { case '<': if (mode & (HASTABLE | HASPORT)) return NULL; mode |= (HASPORT | INPORT); reclblp++; break; case '>': if (!(mode & INPORT)) return NULL; mode &= ~INPORT; reclblp++; break; case '{': reclblp++; if (mode != 0 || !*reclblp) return NULL; mode = HASTABLE; if (!(rv->fld[fi++] = parse_reclbl (n, NOT (LR) , FALSE, text))) return NULL; break; case '}': case '|': case '\000': if ((!*reclblp && !flag) || (mode & INPORT)) return NULL; if (!(mode & HASTABLE)) fp = rv->fld[fi++] = NEW (field_t); if (mode & HASPORT) { if (psp > &port[0] + 1 && psp - 1 != hspsp && *(psp - 1) == ' ') psp--; *psp = '\000'; fp->id = strdup (&port[0]); hspsp = psp = &port[0]; } if (!(mode & (HASTEXT | HASTABLE))) mode |= HASTEXT, *tsp++ = ' '; if (mode & HASTEXT) { if (tsp > text + 1 && tsp - 1 != hstsp && *(tsp - 1) == ' ') tsp--; *tsp = '\000'; fp->lp = make_label (strdup (text), ND_label(n)->fontsize, ND_label(n)->fontname, ND_label(n)->fontcolor,n->graph); fp->LR = TRUE; hstsp = tsp = text; } if (*reclblp) { if (*reclblp == '}') { reclblp++; rv->n_flds = fi; return rv; } mode = 0; reclblp++; } else wflag = FALSE; break; case '\\': if (*(reclblp + 1)) { if (ISCTRL (*(reclblp + 1))) reclblp++; else if (*(reclblp + 1) == ' ') ishardspace = TRUE, reclblp++; } /* falling through ... */ default: if ((mode & HASTABLE) && *reclblp != ' ') return NULL; if (!(mode & (INTEXT | INPORT)) && *reclblp != ' ') mode |= (INTEXT | HASTEXT); if (mode & INTEXT) { if (!(*reclblp == ' ' && !ishardspace && *(tsp - 1) == ' ')) *tsp++ = *reclblp; if (ishardspace) hstsp = tsp - 1; } else if (mode & INPORT) { if (!(*reclblp == ' ' && !ishardspace && (psp == &port[0] || *(psp - 1) == ' '))) *psp++ = *reclblp; if (ishardspace) hspsp = psp - 1; } reclblp++; break; } } rv->n_flds = fi; return rv; } static point size_reclbl(node_t* n, field_t* f) { int i; point d,d0; if (f->lp) d = cvt2pt(f->lp->dimen); else { d.x = d.y = 0.0; for (i = 0; i < f->n_flds; i++) { d0 = size_reclbl(n,f->fld[i]); if (f->LR) { d.x += d0.x; d.y = MAX(d.y,d0.y); } else { d.y += d0.y; d.x = MAX(d.x,d0.x); } } } f->size = d; return d; } static void resize_reclbl(field_t* f, point sz) { int i,amt; double inc; point d,newsz; field_t *sf; /* adjust field */ d.x = sz.x - f->size.x; d.y = sz.y - f->size.y; f->size = sz; /* adjust children */ if (f->n_flds) { if (f->LR) inc = (double)d.x/f->n_flds; else inc = (double)d.y/f->n_flds; for (i = 0; i < f->n_flds; i++) { sf = f->fld[i]; amt = ((int)((i+1)*inc)) - ((int)(i*inc)); if (f->LR) newsz = pointof(sf->size.x+amt,sz.y); else newsz = pointof(sz.x,sf->size.y+amt); resize_reclbl(sf,newsz); } } } static void pos_reclbl(field_t* f, point ul) { int i; f->b.LL = pointof(ul.x,ul.y-f->size.y); f->b.UR = pointof(ul.x+f->size.x,ul.y); for (i = 0; i < f->n_flds; i++) { pos_reclbl(f->fld[i],ul); if (f->LR) ul.x = ul.x + f->fld[i]->size.x; else ul.y = ul.y - f->fld[i]->size.y; } } /* syntax of labels: foo|bar|baz or foo|(recursive|label)|baz */ void record_init(node_t* n) { field_t *info; point ul,sz; int len; char* textbuf; /* temp buffer for storing labels */ reclblp = ND_label(n)->text; len = strlen (reclblp); textbuf = N_NEW(len+1,char); if (!(info = parse_reclbl(n,NOT(GD_left_to_right(n->graph)), TRUE, textbuf))) { agerr(AGERR, "bad label format %s\n", ND_label(n)->text); reclblp = "\\N"; info = parse_reclbl(n,NOT(GD_left_to_right(n->graph)), TRUE, textbuf); } free (textbuf); size_reclbl(n,info); sz.x = POINTS(ND_width(n)); sz.y = POINTS(ND_height(n)); if (mapbool(late_string(n,N_fixed,"false"))) { if ((sz.x < info->size.x) || (sz.y < info->size.y)) { /* should check that the record really won't fit, e.g., there may be no text. agerr(AGWARN, "node '%s' size may be too small\n", n->name); */ } } else { sz.x = MAX(info->size.x,sz.x); sz.y = MAX(info->size.y,sz.y); } resize_reclbl(info,sz); ul = pointof(-sz.x/2,sz.y/2); pos_reclbl(info,ul); ND_width(n) = PS2INCH(info->size.x); ND_height(n) = PS2INCH(info->size.y); ND_shape_info(n) = (void*) info; } static void record_free(node_t* n) { field_t* p = ND_shape_info(n); free(p); } static field_t * map_rec_port(field_t* f, char* str) { field_t *rv; int sub; if (f->id && (strcmp(f->id,str) == 0)) rv = f; else { rv = NULL; for (sub = 0; sub < f->n_flds; sub++) if ((rv = map_rec_port(f->fld[sub],str))) break; } return rv; } static port_t record_port(node_t* n, char* pname) { field_t *f; box b; port_t rv; if (pname[0] != ':') return Center; /*could be '\000' */ if ((f = map_rec_port((field_t*) ND_shape_info(n),pname+1)) == NULL) { unrecognized(n,pname); return Center; } b = f->b; rv.p = pointof((b.LL.x+b.UR.x)/2,(b.LL.y+b.UR.y)/2); if (GD_left_to_right(n->graph)) rv.p = invflip_pt(rv.p); rv.order = (MC_SCALE * (ND_lw_i(n) + rv.p.x)) / (ND_lw_i(n) + ND_rw_i(n)); rv.constrained = FALSE; rv.defined = TRUE; return rv; } static int record_inside(node_t* n, pointf p, edge_t* e) { pointf LL,UR; edge_t *f; field_t *fld0; char *pname; static edge_t *last_e; static node_t *last_n; static field_t *fld; if (GD_left_to_right(n->graph)) p = flip_ptf(p); for (f = e; ED_edge_type(f) != NORMAL; f = ED_to_orig(f)); e = f; if ((e != last_e) || (n != last_n)) { last_e = e; last_n = n; pname = agget(e,(n == f->head ? "headport" : "tailport")); fld = map_rec_port((field_t*)ND_shape_info(n),pname+1); } if (fld == NULL) { fld0 = (field_t*) ND_shape_info(n); UR.x = fld0->size.x / 2.0; LL.x = -UR.x; UR.y = fld0->size.y / 2.0; LL.y = -UR.y; } else { LL.x = fld->b.LL.x; LL.y = fld->b.LL.y; UR.x = fld->b.UR.x; UR.y = fld->b.UR.y; } return (BETWEEN(LL.x,p.x,UR.x) && BETWEEN(LL.y,p.y,UR.y)); } static box flip_rec_box(box b, point p) { box rv; /* flip box */ rv.UR.x = b.UR.y; rv.UR.y = b.UR.x; rv.LL.x = b.LL.y; rv.LL.y = b.LL.x; /* move box */ rv.LL.x += p.x; rv.LL.y += p.y; rv.UR.x += p.x; rv.UR.y += p.y; return rv; } static int record_path(node_t* n, edge_t* e, int pt, box rv[], int* kptr) { int i,side,ls,rs; point p; field_t *info; if (pt == 1) p = ED_tail_port(e).p; else p = ED_head_port(e).p; info = (field_t*) ND_shape_info(n); for (i = 0; i < info->n_flds; i++) { if (GD_left_to_right(n->graph) == FALSE) { ls = info->fld[i]->b.LL.x; rs = info->fld[i]->b.UR.x; } else { ls = info->fld[i]->b.LL.y; rs = info->fld[i]->b.UR.y; } if (BETWEEN(ls,p.x,rs)) { /* FIXME: I don't understand this code */ if (GD_left_to_right(n->graph)) { rv[0] = flip_rec_box(info->fld[i]->b,ND_coord_i(n)); } else { rv[0].LL.x = ND_coord_i(n).x + ls; rv[0].LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; rv[0].UR.x = ND_coord_i(n).x + rs; } #if 0 s0 = (rv[0].UR.x - rv[0].LL.x)/6; s0 = MIN(s0,n->GD_nodesep(graph)); s1 = MIN(p.x - rv[0].LL.x,rv[0].UR.x - p.x)/2; sep = MIN(s0,s1); rv[0].LL.x += sep; rv[0].UR.x -= sep; #endif rv[0].UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; *kptr = 1; break; } } if (pt == 1) side = BOTTOM; else side = TOP; return side; } static void gen_fields(node_t* n, field_t* f) { int i; double cx,cy; point A[2]; if (f->lp) { cx = (f->b.LL.x + f->b.UR.x)/2.0 + ND_coord_i(n).x; cy = (f->b.LL.y + f->b.UR.y)/2.0 + ND_coord_i(n).y; f->lp->p = pointof((int)cx,(int)cy); emit_label(f->lp,n->graph); } /* yes it is ridiculous that black is hardwired here, the same way * it is wired into psgen.c ... outline color should be adjustable */ /* for reasons not presently remembered, we used to say CodeGen->set_color("black"); right here */ for (i = 0; i < f->n_flds; i++) { if (i > 0) { if (f->LR) { A[0] = f->fld[i]->b.LL; A[1].x = A[0].x; A[1].y = f->fld[i]->b.UR.y; } else { A[1] = f->fld[i]->b.UR; A[0].x = f->fld[i]->b.LL.x; A[0].y = A[1].y; } A[0] = add_points(A[0],ND_coord_i(n)); A[1] = add_points(A[1],ND_coord_i(n)); CodeGen->polyline(A,2); } gen_fields(n,f->fld[i]); } } static void record_gencode(node_t* n) { point A[4]; int i,style; field_t *f; f = (field_t*) ND_shape_info(n); A[0] = f->b.LL; A[2] = f->b.UR; A[1].x = A[2].x; A[1].y = A[0].y; A[3].x = A[0].x; A[3].y = A[2].y; for (i = 0; i < 4; i++) A[i] = add_points(A[i],ND_coord_i(n)); CodeGen->begin_node(n); CodeGen->begin_context(); style = stylenode(n); pencolor(n); if (style & FILLED) fillcolor(n); /* emit fill color */ if (streq(ND_shape(n)->name,"Mrecord")) style |= ROUNDED; if (style & (ROUNDED | DIAGONALS)) round_corners(n,A,4,ROUNDED); else CodeGen->polygon(A,4,style&FILLED); gen_fields(n,f); CodeGen->end_context(); CodeGen->end_node(); } static shape_desc **UserShape; static int N_UserShape; shape_desc *find_user_shape(char* name) { int i; if (UserShape) { for (i = 0; i < N_UserShape; i++) { if (streq(UserShape[i]->name,name)) return UserShape[i]; } } return NULL; } static shape_desc *user_shape(char* name) { int i; shape_desc *p; if ((p = find_user_shape(name))) return p; i = N_UserShape++; UserShape = ALLOC(N_UserShape,UserShape,shape_desc*); p = UserShape[i] = NEW(shape_desc); *p = Shapes[0]; p->name = name; p->usershape = TRUE; if ((Lib == NULL) && (!CodeGen || (CodeGen->usershapesize == NULL))) agerr(AGWARN, "using %s for unknown shape %s\n", Shapes[0].name,p->name); return p; } shape_desc * bind_shape(char* name) { shape_desc *ptr,*rv= NULL; for (ptr = Shapes; ptr->name; ptr++) if (!strcmp(ptr->name,name)) {rv = ptr; break;} if (rv == NULL) rv = user_shape(name); return rv; } static int epsf_inside(node_t* n, pointf p, edge_t* e) { pointf P; double x2; P = (GD_left_to_right(n->graph)? flip_ptf(p) : p); x2 = ND_ht_i(n) / 2; return ((P.y >= -x2) && (P.y <= x2) && (P.x >= -ND_lw_i(n)) && (P.x <= ND_rw_i(n))); }