From: ellson Date: Mon, 31 Jul 2006 17:46:44 +0000 (+0000) Subject: collect together three graphics-state stacks into one place so that the can be unified X-Git-Tag: LAST_LIBGRAPH~32^2~6016 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b510c2966177411ca489f8bbde01c87c0a1b5f85;p=graphviz collect together three graphics-state stacks into one place so that the can be unified --- diff --git a/lib/common/emit.c b/lib/common/emit.c index d5c6d851f..6ba83d79d 100644 --- a/lib/common/emit.c +++ b/lib/common/emit.c @@ -18,14 +18,400 @@ * graphics code generator */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include #include #include "render.h" #include "agxbuf.h" +#include "htmltable.h" +#define P2RECT(p, pr, sx, sy) (pr[0].x = p.x - sx, pr[0].y = p.y - sy, pr[1].x = p.x + sx, pr[1].y = p.y + sy) +#define FUZZ 3 #define EPSILON .0001 static char *defaultlinestyle[3] = { "solid\0", "setlinewidth\0001\0", 0 }; -int emitState; + +/* push empty graphic state for current object */ +static obj_state_t* push_obj_state(GVJ_t *job) +{ + obj_state_t *obj; + + if (! (obj = zmalloc(sizeof(obj_state_t)))) + agerr(AGERR, "no memory from zmalloc()\n"); + + obj->parent = job->obj; + job->obj = obj; + + return obj; +} + +/* pop graphic state of current object */ +static void pop_obj_state(GVJ_t *job) +{ + obj_state_t *obj = job->obj; + + assert(obj); + + if (obj->url) free(obj->url); + if (obj->tailurl) free(obj->tailurl); + if (obj->headurl) free(obj->headurl); + if (obj->tooltip) free(obj->tooltip); + if (obj->tailtooltip) free(obj->tailtooltip); + if (obj->headtooltip) free(obj->headtooltip); + if (obj->target) free(obj->target); + if (obj->tailtarget) free(obj->tailtarget); + if (obj->headtarget) free(obj->headtarget); + if (obj->url_map_p) free(obj->url_map_p); + if (obj->url_bsplinemap_p) free(obj->url_bsplinemap_p); + if (obj->url_bsplinemap_n) free(obj->url_bsplinemap_n); + if (obj->tailurl_map_p) free(obj->tailurl_map_p); + if (obj->headurl_map_p) free(obj->headurl_map_p); + + job->obj = obj->parent; + free(obj); +} + +static void doHTMLdata(htmldata_t * dp, point p, void *obj) +{ + char *url = NULL, *target = NULL, *title = NULL; + pointf p1, p2; + int havetitle = 0; + + if ((url = dp->href) && url[0]) { + switch (agobjkind(obj)) { + case AGGRAPH: + url = strdup_and_subst_graph(url, (graph_t *) obj); + break; + case AGNODE: + url = strdup_and_subst_node(url, (node_t *) obj); + break; + case AGEDGE: + url = strdup_and_subst_edge(url, (edge_t *) obj); + break; + } + } + target = dp->target; + if ((title = dp->title) && title[0]) { + havetitle++; + switch (agobjkind(obj)) { + case AGGRAPH: + title = strdup_and_subst_graph(title, (graph_t *) obj); + break; + case AGNODE: + title = strdup_and_subst_node(title, (node_t *) obj); + break; + case AGEDGE: + title = strdup_and_subst_edge(title, (edge_t *) obj); + break; + } + } + if (url || title) { + p1.x = p.x + dp->box.LL.x; + p1.y = p.y + dp->box.LL.y; + p2.x = p.x + dp->box.UR.x; + p2.y = p.y + dp->box.UR.y; +// FIXME +// map_output_rect(p1, p2, url, target, "", title); + } + free(url); + free(title); +} + +/* forward declaration */ +static void doHTMLcell(htmlcell_t * cp, point p, void *obj); + +static void doHTMLtbl(htmltbl_t * tbl, point p, void *obj) +{ + htmlcell_t **cells = tbl->u.n.cells; + htmlcell_t *cp; + + while ((cp = *cells++)) + doHTMLcell(cp, p, obj); + if (tbl->data.href) + doHTMLdata(&tbl->data, p, obj); +} + +static void doHTMLcell(htmlcell_t * cp, point p, void *obj) +{ + if (cp->child.kind == HTML_TBL) + doHTMLtbl(cp->child.u.tbl, p, obj); + if (cp->data.href) + doHTMLdata(&cp->data, p, obj); +} + +static void doHTMLlabel(htmllabel_t * lbl, point p, void *obj) +{ + if (lbl->kind == HTML_TBL) { + doHTMLtbl(lbl->u.tbl, p, obj); + } +} + +/* isRect: + * isRect function returns true when polygon has + * regular rectangular shape. Rectangle is regular when + * it is not skewed and distorted and orientation is almost zero + */ +static bool isRect(polygon_t * p) +{ + return (p->sides == 4 && (ROUND(p->orientation) % 90) == 0 + && p->distortion == 0.0 && p->skew == 0.0); +} + +/* + * isFilled function returns 1 if filled style has been set for node 'n' + * otherwise returns 0. it accepts pointer to node_t as an argument + */ +static int ifFilled(node_t * n) +{ + char *style, *p, **pp; + int r = 0; + style = late_nnstring(n, N_style, ""); + if (style[0]) { + pp = parse_style(style); + while ((p = *pp)) { + if (strcmp(p, "filled") == 0) + r = 1; + pp++; + } + } + return r; +} + +/* pEllipse: + * pEllipse function returns 'np' points from the circumference + * of ellipse described by radii 'a' and 'b'. + * Assumes 'np' is greater than zero. + * 'np' should be at least 4 to sample polygon from ellipse + */ +static pointf *pEllipse(double a, double b, int np) +{ + double theta = 0.0; + double deltheta = 2 * M_PI / np; + int i; + pointf *ps; + + ps = N_NEW(np, pointf); + for (i = 0; i < np; i++) { + ps[i].x = a * cos(theta); + ps[i].y = b * sin(theta); + theta += deltheta; + } + return ps; +} + +#define HW 2.0 /* maximum distance away from line, in points */ + +/* check_control_points: + * check_control_points function checks the size of quadrilateral + * formed by four control points + * returns 1 if four points are in line (or close to line) + * else return 0 + */ +static int check_control_points(pointf *cp) +{ + double dis1 = ptToLine2 (cp[0], cp[3], cp[1]); + double dis2 = ptToLine2 (cp[0], cp[3], cp[2]); + if (dis1 < HW*HW && dis2 < HW*HW) + return 1; + else + return 0; +} + +#ifdef DEBUG +static void psmapOutput (point* ps, int n) +{ + int i; + fprintf (stdout, "newpath %d %d moveto\n", ps[0].x, ps[0].y); + for (i=1; i < n; i++) + fprintf (stdout, "%d %d lineto\n", ps[i].x, ps[i].y); + fprintf (stdout, "closepath stroke\n"); +} +#endif + +typedef struct segitem_s { + pointf p; + struct segitem_s* next; +} segitem_t; + +#define MARK_FIRST_SEG(L) ((L)->next = (segitem_t*)1) +#define FIRST_SEG(L) ((L)->next == (segitem_t*)1) +#define INIT_SEG(P,L) {(L)->next = 0; (L)->p = P;} + +static segitem_t* appendSeg (pointf p, segitem_t* lp) +{ + segitem_t* s = GNEW(segitem_t); + INIT_SEG (p, s); + lp->next = s; + return s; +} + +/* map_bspline_poly: + * Output the polygon determined by the n points in p1, followed + * by the n points in p2 in reverse order. Assumes n <= 50. + */ +static void map_bspline_poly(pointf **pbs_p, int **pbs_n, int *pbs_poly_n, int n, pointf* p1, pointf* p2) +{ + int i = 0, nump = 0, last = 2*n-1; + + for ( ; i < *pbs_poly_n; i++) + nump += (*pbs_n)[i]; + + (*pbs_poly_n)++; + *pbs_n = grealloc(*pbs_n, (*pbs_poly_n) * sizeof(int)); + (*pbs_n)[i] = 2*n; + *pbs_p = grealloc(*pbs_p, (nump + 2*n) * sizeof(pointf)); + + for (i = 0; i < n; i++) { + (*pbs_p)[nump+i] = p1[i]; + (*pbs_p)[nump+last-i] = p2[i]; + } +#ifdef DEBUG + psmapOutput (*pbs_p + nump, last+1); +#endif +} + +/* approx_bezier: + * Approximate Bezier by line segments. If the four points are + * almost colinear, as determined by check_control_points, we store + * the segment cp[0]-cp[3]. Otherwise we split the Bezier into 2 and recurse. + * Since 2 contiguous segments share an endpoint, we actually store + * the segments as a list of points. + * New points are appended to the list given by lp. The tail of the + * list is returned. + */ +static segitem_t* approx_bezier (pointf *cp, segitem_t* lp) +{ + pointf sub_curves[8]; + + if (check_control_points(cp)) { + if (FIRST_SEG (lp)) INIT_SEG (cp[0], lp); + lp = appendSeg (cp[3], lp); + } + else { + Bezier (cp, 3, 0.5, sub_curves, sub_curves+4); + lp = approx_bezier (sub_curves, lp); + lp = approx_bezier (sub_curves+4, lp); + } + return lp; +} + +/* bisect: + * Return the angle of the bisector between the two rays + * pp-cp and cp-np. The bisector returned is always to the + * left of pp-cp-np. + */ +static double bisect (pointf pp, pointf cp, pointf np) +{ + double ang, theta, phi; + theta = atan2(np.y - cp.y,np.x - cp.x); + phi = atan2(pp.y - cp.y,pp.x - cp.x); + ang = theta - phi; + if (ang > 0) ang -= 2*M_PI; + + return (phi + ang/2.0); +} + +/* mkSegPts: + * Determine polygon points related to 2 segments prv-cur and cur-nxt. + * The points lie on the bisector of the 2 segments, passing through cur, + * and distance HW from cur. The points are stored in p1 and p2. + * If p1 is NULL, we use the normal to cur-nxt. + * If p2 is NULL, we use the normal to prv-cur. + * Assume at least one of prv or nxt is non-NULL. + */ +static void mkSegPts (segitem_t* prv, segitem_t* cur, segitem_t* nxt, + pointf* p1, pointf* p2) +{ + pointf cp, pp, np; + double theta, delx, dely; + pointf p; + + cp = cur->p; + /* if prv or nxt are NULL, use the one given to create a collinear + * prv or nxt. This could be more efficiently done with special case code, + * but this way is more uniform. + */ + if (prv) { + pp = prv->p; + if (nxt) + np = nxt->p; + else { + np.x = 2*cp.x - pp.x; + np.y = 2*cp.y - pp.y; + } + } + else { + np = nxt->p; + pp.x = 2*cp.x - np.x; + pp.y = 2*cp.y - np.y; + } + theta = bisect(pp,cp,np); + delx = HW*cos(theta); + dely = HW*sin(theta); + p.x = cp.x + delx; + p.y = cp.y + dely; + *p1 = p; + p.x = cp.x - delx; + p.y = cp.y - dely; + *p2 = p; +} + +/* map_output_bspline: + * Construct and output a closed polygon approximating the input + * B-spline bp. We do this by first approximating bp by a sequence + * of line segments. We then use the sequence of segments to determine + * the polygon. + * In cmapx, polygons are limited to 100 points, so we output polygons + * in chunks of 100. + */ +static void map_output_bspline (pointf **pbs, int **pbs_n, int *pbs_poly_n, bezier* bp) +{ + segitem_t* segl = GNEW(segitem_t); + segitem_t* segp = segl; + segitem_t* segprev; + segitem_t* segnext; + int nc, j, k, cnt; + pointf pts[4]; + pointf pt1[50], pt2[50]; + + MARK_FIRST_SEG(segl); + nc = (bp->size - 1)/3; /* nc is number of bezier curves */ + for (j = 0; j < nc; j++) { + for (k = 0; k < 4; k++) { + pts[k].x = (double)bp->list[3*j + k].x; + pts[k].y = (double)bp->list[3*j + k].y; + } + segp = approx_bezier (pts, segp); + } + + segp = segl; + segprev = 0; + cnt = 0; + while (segp) { + segnext = segp->next; + mkSegPts (segprev, segp, segnext, pt1+cnt, pt2+cnt); + cnt++; + if ((segnext == NULL) || (cnt == 50)) { + map_bspline_poly (pbs, pbs_n, pbs_poly_n, cnt, pt1, pt2); + pt1[0] = pt1[cnt-1]; + pt2[0] = pt2[cnt-1]; + cnt = 1; + } + segprev = segp; + segp = segnext; + } + + /* free segl */ + while (segl) { + segp = segl->next; + free (segl); + segl = segp; + } +} + /* parse_layers: * Split input string into tokens, with separators specified by @@ -544,36 +930,207 @@ static bool node_in_box(node_t *n, boxf b) } #endif +static void emit_begin_node(GVJ_t * job, node_t * n) +{ + obj_state_t *obj; + int flags = job->flags; + textlabel_t *lab; + int sides, peripheries, i, j, filled = 0, rect = 0, shape, nump = 0; + polygon_t *poly = NULL; + pointf *vertices, ldimen, *p = NULL; + point coord; + char *s; + + obj = push_obj_state(job); + obj->type = NODE_OBJTYPE; + obj->u.n = n; + + obj->oldstate = job->gvc->emit_state; + job->gvc->emit_state = EMIT_NDRAW; + + if (flags & GVRENDER_DOES_Z) { + obj->z = late_double(n, N_z, 0.0, -MAXFLOAT); + } + if ((flags & GVRENDER_DOES_LABELS) && ((lab = ND_label(n)))) { + if (lab->html) + doHTMLlabel(lab->u.html, lab->p, (void *) n); + obj->label = lab->text; + } + if ((flags & GVRENDER_DOES_MAPS) + && (((s = agget(n, "href")) && s[0]) || ((s = agget(n, "URL")) && s[0]))) { + obj->url = strdup_and_subst_node(s, n); + } + if (flags & GVRENDER_DOES_TOOLTIPS) { + if ((s = agget(n, "tooltip")) && s[0]) { + obj->tooltip = strdup_and_subst_node(s, n); + obj->explicit_tooltip = true; + } + else { + obj->tooltip = strdup(ND_label(n)->text); + } + } + if ((flags & GVRENDER_DOES_TARGETS) && ((s = agget(n, "target")) && s[0])) { + obj->target = strdup_and_subst_node(s, n); + } + if ((flags & (GVRENDER_DOES_MAPS | GVRENDER_DOES_TOOLTIPS)) + && (obj->url || obj->explicit_tooltip)) { + + /* checking shape of node */ + shape = shapeOf(n); + /* node coordinate */ + coord = ND_coord_i(n); + /* checking if filled style has been set for node */ + filled = ifFilled(n); + + if (shape == SH_POLY || shape == SH_POINT) { + poly = (polygon_t *) ND_shape_info(n); + + /* checking if polygon is regular rectangle */ + if (isRect(poly) && (poly->peripheries || filled)) + rect = 1; + } + + /* When node has polygon shape and requested output supports polygons + * we use a polygon to map the clickable region that is a: + * circle, ellipse, polygon with n side, or point. + * For regular rectangular shape we have use node's bounding box to map clickable region + */ + if (poly && !rect && (flags & GVRENDER_DOES_MAP_POLYGON)) { + + if (poly->sides < 3) + sides = 1; + else + sides = poly->sides; + + if (poly->peripheries < 2) + peripheries = 1; + else + peripheries = poly->peripheries; + + vertices = poly->vertices; + + if ((s = agget(n, "samplepoints"))) + nump = atoi(s); + /* We want at least 4 points. For server-side maps, at most 100 + * points are allowed. To simplify things to fit with the 120 points + * used for skewed ellipses, we set the bound at 60. + */ + if ((nump < 4) || (nump > 60)) + nump = DFLT_SAMPLE; + /* use bounding box of text label for mapping + * when polygon has no peripheries and node is not filled + */ + if (poly->peripheries == 0 && !filled) { + obj->url_map_shape = MAP_RECTANGLE; + nump = 2; + p = N_NEW(nump, pointf); + ldimen = ND_label(n)->dimen; + P2RECT(coord, p, ldimen.x / 2.0, ldimen.y / 2.0); + } + /* circle or ellipse */ + else if (poly->sides < 3 && poly->skew == 0.0 && poly->distortion == 0.0) { + if (poly->regular) { + obj->url_map_shape = MAP_CIRCLE; + nump = 2; /* center of circle and top right corner of bb */ + p = N_NEW(nump, pointf); + p[0].x = coord.x; + p[0].y = coord.y; + p[1].x = coord.x + vertices[peripheries - 1].x; + p[1].y = coord.y + vertices[peripheries - 1].y; + } + else { /* ellipse is treated as polygon */ + obj->url_map_shape= MAP_POLYGON; + p = pEllipse((double)(vertices[peripheries - 1].x), + (double)(vertices[peripheries - 1].y), nump); + for (i = 0; i < nump; i++) { + p[i].x += coord.x; + p[i].y += coord.y; + } + } + } + /* all other polygonal shape */ + else { + int offset = (peripheries - 1)*(poly->sides); + obj->url_map_shape = MAP_POLYGON; + /* distorted or skewed ellipses and circles are polygons with 120 + * sides. For mapping we convert them into polygon with sample sides + */ + if (poly->sides >= nump) { + int delta = poly->sides / nump; + p = N_NEW(nump, pointf); + for (i = 0, j = 0; j < nump; i += delta, j++) { + p[j].x = coord.x + vertices[i + offset].x; + p[j].y = coord.y + vertices[i + offset].y; + } + } else { + nump = sides; + p = N_NEW(nump, pointf); + for (i = 0; i < nump; i++) { + p[i].x = coord.x + vertices[i + offset].x; + p[i].y = coord.y + vertices[i + offset].y; + } + } + } + } + else { + /* we have to use the node's bounding box to map clickable region + * when requested output format is not capable of polygons. + */ + obj->url_map_shape = MAP_RECTANGLE; + nump = 2; + p = N_NEW(nump, pointf); + p[0].x = coord.x - ND_lw_i(n); + p[0].y = coord.y - (ND_ht_i(n) / 2); + p[1].x = coord.x + ND_rw_i(n); + p[1].y = coord.y + (ND_ht_i(n) / 2); + } + if (! (flags & GVRENDER_DOES_TRANSFORM)) + gvrender_ptf_A(job, p, p, nump); + obj->url_map_p = p; + obj->url_map_n = nump; + } + +#ifdef WITH_CODEGENS + Obj = NODE; +#endif + gvrender_begin_node(job, n); + setColorScheme (agget (n, "colorscheme")); + gvrender_begin_context(job); +} + +static void emit_end_node(GVJ_t * job) +{ + gvrender_end_context(job); + gvrender_end_node(job); +#ifdef WITH_CODEGENS + Obj = NONE; +#endif + job->gvc->emit_state = job->obj->oldstate; + pop_obj_state(job); +} + static void emit_node(GVJ_t * job, node_t * n) { GVC_t *gvc = job->gvc; char *s; - int oldstate; if (ND_shape(n) == NULL) return; - oldstate = gvc->emit_state; - gvc->emit_state = EMIT_NDRAW; if (node_in_layer(job, n->graph, n) && node_in_box(n, job->pageBoxClip) && (ND_state(n) != gvc->common.viewNum)) { gvrender_comment(job, n->name); - s = late_string(n, N_comment, ""); if (s[0]) gvrender_comment(job, s); - gvrender_begin_node(job, n); - setColorScheme (agget (n, "colorscheme")); - gvrender_begin_context(job); + emit_begin_node(job, n); ND_shape(n)->fns->codefn(job, n); ND_state(n) = gvc->common.viewNum; - gvrender_end_context(job); - gvrender_end_node(job); + emit_end_node(job); } - gvc->emit_state = oldstate; } /* calculate an offset vector, length d, perpendicular to line p,q */ @@ -721,12 +1278,9 @@ void emit_edge_graphics(GVJ_t * job, edge_t * e) bool saved = FALSE; double scale, numc2; char *p; - int oldstate; #define SEP 2.0 - oldstate = job->gvc->emit_state; - job->gvc->emit_state = EMIT_EDRAW; style = late_string(e, E_style, ""); /* We shortcircuit drawing an invisible edge because the arrowhead * code resets the style to solid, and most of the code generators @@ -928,7 +1482,6 @@ void emit_edge_graphics(GVJ_t * job, edge_t * e) if (saved) gvrender_end_context(job); - job->gvc->emit_state = oldstate; } static bool edge_in_box(edge_t *e, boxf b) @@ -947,16 +1500,215 @@ static bool edge_in_box(edge_t *e, boxf b) return FALSE; } +static void emit_begin_edge(GVJ_t * job, edge_t * e) +{ + obj_state_t *obj; + int flags = job->flags; + char *s; + textlabel_t *lab = NULL, *tlab = NULL, *hlab = NULL; + pointf *p = NULL, *pt = NULL, *ph = NULL, *pte = NULL, *phe = NULL, *pbs = NULL; + int i, nump, *pbs_n = NULL, pbs_poly_n = 0; + bezier bz; + + obj = push_obj_state(job); + obj->type = EDGE_OBJTYPE; + obj->u.e = e; + + obj->oldstate = job->gvc->emit_state; + job->gvc->emit_state = EMIT_EDRAW; + + if (flags & GVRENDER_DOES_Z) { + obj->tail_z= late_double(e->tail, N_z, 0.0, -1000.0); + obj->head_z= late_double(e->head, N_z, 0.0, -MAXFLOAT); + } + + if (flags & GVRENDER_DOES_LABELS) { + if ((lab = ED_label(e))) { + if (lab->html) + doHTMLlabel(lab->u.html, lab->p, (void *) e); + obj->label = lab->text; + } + obj->taillabel = obj->headlabel = obj->label; + if ((tlab = ED_tail_label(e))) { + if (tlab->html) + doHTMLlabel(tlab->u.html, tlab->p, (void *) e); + obj->taillabel = tlab->text; + } + if ((hlab = ED_head_label(e))) { + if (hlab->html) + doHTMLlabel(hlab->u.html, hlab->p, (void *) e); + obj->headlabel = hlab->text; + } + } + + if (flags & GVRENDER_DOES_MAPS) { + if (((s = agget(e, "href")) && s[0]) || ((s = agget(e, "URL")) && s[0])) + obj->url = strdup_and_subst_edge(s, e); + if (((s = agget(e, "tailhref")) && s[0]) || ((s = agget(e, "tailURL")) && s[0])) + obj->tailurl = strdup_and_subst_edge(s, e); + else if (obj->url) + obj->tailurl = strdup(obj->url); + if (((s = agget(e, "headhref")) && s[0]) || ((s = agget(e, "headURL")) && s[0])) + obj->headurl = strdup_and_subst_edge(s, e); + else if (obj->url) + obj->headurl = strdup(obj->url); + } + + if (flags & GVRENDER_DOES_TARGETS) { + if ((s = agget(e, "target")) && s[0]) + obj->target = strdup_and_subst_edge(s, e); + if ((s = agget(e, "tailtarget")) && s[0]) + obj->tailtarget = strdup_and_subst_edge(s, e); + else if (obj->target) + obj->tailtarget = strdup(obj->target); + if ((s = agget(e, "headtarget")) && s[0]) + obj->headtarget = strdup_and_subst_edge(s, e); + else if (obj->target) + obj->headtarget = strdup(obj->target); + } + + if (flags & GVRENDER_DOES_TOOLTIPS) { + if ((s = agget(e, "tooltip")) && s[0]) { + obj->tooltip = strdup_and_subst_edge(s, e); + obj->explicit_tooltip = true; + } + else if (obj->label) + obj->tooltip = strdup(obj->label); + if ((s = agget(e, "tailtooltip")) && s[0]) { + obj->tailtooltip = strdup_and_subst_edge(s, e); + obj->explicit_tailtooltip = true; + } + else if (obj->taillabel) + obj->tailtooltip = strdup(obj->taillabel); + if ((s = agget(e, "headtooltip")) && s[0]) { + obj->headtooltip = strdup_and_subst_edge(s, e); + obj->explicit_headtooltip = true; + } + else if (obj->headlabel) + obj->headtooltip = strdup(obj->headlabel); + } + + if (flags & (GVRENDER_DOES_MAPS | GVRENDER_DOES_TOOLTIPS)) { + if (flags & (GVRENDER_DOES_MAP_RECTANGLE | GVRENDER_DOES_MAP_POLYGON)) { + if (flags & GVRENDER_DOES_MAP_RECTANGLE) { + obj->url_map_shape = MAP_RECTANGLE; + nump = 2; + } + else { /* GVRENDER_DOES_MAP_POLYGON */ + obj->url_map_shape = MAP_POLYGON; + nump = 4; + } + + if (lab && (obj->url || obj->tooltip)) { + obj->url_map_n = nump; + p = N_NEW(nump, pointf); + P2RECT(lab->p, p, lab->dimen.x / 2., lab->dimen.y / 2.); + } + + if (tlab && (obj->tailurl || obj->tailtooltip)) { + obj->tailurl_map_n = nump; + pt = N_NEW(nump, pointf); + P2RECT(tlab->p, pt, tlab->dimen.x / 2., tlab->dimen.y / 2.); + } + + if (hlab && (obj->headurl || obj->headtooltip)) { + obj->headurl_map_n = nump; + ph = N_NEW(nump, pointf); + P2RECT(hlab->p, ph, hlab->dimen.x / 2., hlab->dimen.y / 2.); + } + + /* process intersecion with tail node */ + if (ED_spl(e) && (obj->tailurl || obj->tailtooltip)) { + obj->tailendurl_map_n = nump; + pte = N_NEW(nump, pointf); + bz = ED_spl(e)->list[0]; + if (bz.sflag) { + /* Arrow at start of splines */ + P2RECT(bz.sp, pte, FUZZ, FUZZ); + } else { + /* No arrow at start of splines */ + P2RECT(bz.list[0], pte, FUZZ, FUZZ); + } + } + + /* process intersection with head node */ + if (ED_spl(e) && (obj->headurl || obj->headtooltip)) { + obj->headendurl_map_n = nump; + phe = N_NEW(nump, pointf); + bz = ED_spl(e)->list[ED_spl(e)->size - 1]; + if (bz.eflag) { + /* Arrow at end of splines */ + P2RECT(bz.ep, phe, FUZZ, FUZZ); + } else { + /* No arrow at end of splines */ + P2RECT(bz.list[bz.size - 1], phe, FUZZ, FUZZ); + } + } + + if (ED_spl(e) && (obj->url || obj->tooltip) && (flags & GVRENDER_DOES_MAP_POLYGON)) { + int ns; + splines *spl; + + spl = ED_spl(e); + ns = spl->size; /* number of splines */ + for (i = 0; i < ns; i++) + map_output_bspline (&pbs, &pbs_n, &pbs_poly_n, spl->list+i); + obj->url_bsplinemap_poly_n = pbs_poly_n; + obj->url_bsplinemap_n = pbs_n; + } + + if (! (flags & GVRENDER_DOES_TRANSFORM)) { + if (p) gvrender_ptf_A(job, p, p, 2); + if (pt) gvrender_ptf_A(job, pt, pt, 2); + if (ph) gvrender_ptf_A(job, ph, ph, 2); + if (pte) gvrender_ptf_A(job, pte, pte, 2); + if (phe) gvrender_ptf_A(job, phe, phe, 2); + if (pbs) { + for ( nump = 0, i = 0; i < pbs_poly_n; i++) + nump += pbs_n[i]; + gvrender_ptf_A(job, pbs, pbs, nump); + } + } + if (! (flags & GVRENDER_DOES_MAP_RECTANGLE)) { + if (p) rect2poly(p); + if (pt) rect2poly(pt); + if (ph) rect2poly(ph); + if (pte) rect2poly(pte); + if (phe) rect2poly(phe); + } + + } + obj->url_map_p = p; + obj->tailurl_map_p = pt; + obj->headurl_map_p = ph; + obj->tailendurl_map_p = pte; + obj->headendurl_map_p = phe; + obj->url_bsplinemap_p = pbs; + } + +#ifdef WITH_CODEGENS + Obj = EDGE; +#endif + gvrender_begin_edge(job, e); +} + +static void emit_end_edge(GVJ_t * job) +{ + gvrender_end_edge(job); +#ifdef WITH_CODEGENS + Obj = NONE; +#endif + job->gvc->emit_state = job->obj->oldstate; + pop_obj_state(job); +} + static void emit_edge(GVJ_t * job, edge_t * e) { char *s; - int oldstate; if (! edge_in_box(e, job->pageBoxClip) || ! edge_in_layer(job, e->head->graph, e)) return; - oldstate = job->gvc->emit_state; - job->gvc->emit_state = EMIT_EDRAW; s = malloc(strlen(e->tail->name) + 2 + strlen(e->head->name) + 1); strcpy(s,e->tail->name); if (AG_IS_DIRECTED(e->tail->graph)) @@ -971,10 +1723,9 @@ static void emit_edge(GVJ_t * job, edge_t * e) if (s[0]) gvrender_comment(job, s); - gvrender_begin_edge(job, e); + emit_begin_edge(job, e); emit_edge_graphics (job, e); - gvrender_end_edge(job); - job->gvc->emit_state = oldstate; + emit_end_edge(job); } static void init_gvc(GVC_t * gvc, graph_t * g) @@ -1286,6 +2037,67 @@ void emit_view(GVJ_t * job, graph_t * g, int flags) gvrender_end_page(job); } +static void emit_begin_graph(GVJ_t * job, graph_t * g) +{ + GVC_t *gvc = job->gvc; + int flags = job->flags; + obj_state_t *obj; + textlabel_t *lab; + char *s; + + obj = push_obj_state(job); + obj->type = ROOTGRAPH_OBJTYPE; + obj->u.g = g; + + obj->oldstate = job->gvc->emit_state; + job->gvc->emit_state = EMIT_GDRAW; + + if ((flags & GVRENDER_DOES_LABELS) && ((lab = GD_label(g)))) { + if (lab->html) + doHTMLlabel(lab->u.html, lab->p, (void *) g); + obj->label = lab->text; + } + if ((flags & GVRENDER_DOES_MAPS) + && (((s = agget(g, "href")) && s[0]) + || ((s = agget(g, "URL")) && s[0]))) { + obj->url = strdup_and_subst_graph(s, g); + } + if (flags & GVRENDER_DOES_TOOLTIPS) { + if ((s = agget(g, "tooltip")) && s[0]) { + obj->tooltip = strdup_and_subst_graph(s, g); + obj->explicit_tooltip = true; + } + else if (obj->url && obj->label) { + obj->tooltip = strdup(obj->label); + } + } + if ((flags & GVRENDER_DOES_TARGETS) && ((s = agget(g, "target")) && s[0])) { + obj->target = strdup_and_subst_graph(s, g); + } + + /* init stack */ + gvc->SP = 0; + job->style = &(gvc->styles[0]); + job->style->pen = PEN_SOLID; + job->style->fill = FILL_NONE; + job->style->penwidth = PENWIDTH_NORMAL; + +#ifdef WITH_CODEGENS + Obj = NONE; +#endif + gvrender_begin_graph(job, g); +} + +static void emit_end_graph(GVJ_t * job, graph_t * g) +{ + gvrender_end_graph(job); +#ifdef WITH_CODEGENS + Obj = NONE; +#endif + job->gvc->emit_state = job->obj->oldstate; + pop_obj_state(job); +} + void emit_graph(GVJ_t * job, graph_t * g) { node_t *n; @@ -1298,7 +2110,8 @@ void emit_graph(GVJ_t * job, graph_t * g) s = late_string(g, agfindattr(g, "comment"), ""); gvrender_comment(job, s); - gvrender_begin_graph(job, g); + emit_begin_graph(job, g); + if (flags & EMIT_COLORS) emit_colors(job,g); @@ -1326,7 +2139,7 @@ void emit_graph(GVJ_t * job, graph_t * g) if (job->numLayers > 1) gvrender_end_layer(job); } - gvrender_end_graph(job); + emit_end_graph(job, g); } /* support for stderr_once */ @@ -1397,6 +2210,82 @@ static char **checkClusterStyle(graph_t* sg, int *flagp) return pstyle; } +static void emit_begin_cluster(GVJ_t * job, Agraph_t * sg) +{ + obj_state_t *obj; + int flags = job->flags; + textlabel_t *lab; + char *s; + int nump = 0; + pointf *p = NULL; + + obj = push_obj_state(job); + obj->type = CLUSTER_OBJTYPE; + obj->u.sg = sg; + + obj->oldstate = job->gvc->emit_state; + job->gvc->emit_state = EMIT_CDRAW; + + if ((flags & GVRENDER_DOES_LABELS) && ((lab = GD_label(sg)))) { + if (lab->html) + doHTMLlabel(lab->u.html, lab->p, (void *) sg); + obj->label = lab->text; + } + if ((flags & GVRENDER_DOES_MAPS) + && (((s = agget(sg, "href")) && s[0]) || ((s = agget(sg, "URL")) && s[0]))) + obj->url = strdup_and_subst_graph(s, sg); + + if ((flags & GVRENDER_DOES_TARGETS) && ((s = agget(sg, "target")) && s[0])) + obj->target = strdup_and_subst_graph(s, sg); + + if (flags & GVRENDER_DOES_TOOLTIPS) { + if ((s = agget(sg, "tooltip")) && s[0]) { + obj->tooltip = strdup_and_subst_graph(s, sg); + obj->explicit_tooltip = true; + } + else if (obj->label) { + obj->tooltip = strdup(obj->label); + } + } + + if (flags & (GVRENDER_DOES_MAPS | GVRENDER_DOES_TOOLTIPS)) { + if (flags & (GVRENDER_DOES_MAP_RECTANGLE | GVRENDER_DOES_MAP_POLYGON)) { + if (flags & GVRENDER_DOES_MAP_RECTANGLE) { + obj->url_map_shape = MAP_RECTANGLE; + nump = 2; + } + else { + obj->url_map_shape = MAP_POLYGON; + nump = 4; + } + + p = N_NEW(nump, pointf); + P2PF(GD_bb(sg).LL, p[0]); + P2PF(GD_bb(sg).UR, p[1]); + + if (! (flags & (GVRENDER_DOES_MAP_RECTANGLE))) + rect2poly(p); + } + obj->url_map_p = p; + obj->url_map_n = nump; + } + +#ifdef WITH_CODEGENS + Obj = CLST; +#endif + gvrender_begin_cluster(job, sg); +} + +static void emit_end_cluster(GVJ_t * job, Agraph_t * g) +{ + gvrender_end_cluster(job, g); +#ifdef WITH_CODEGENS + Obj = NONE; +#endif + job->gvc->emit_state = job->obj->oldstate; + pop_obj_state(job); +} + void emit_clusters(GVJ_t * job, Agraph_t * g, int flags) { int c, istyle, filled; @@ -1406,10 +2295,7 @@ void emit_clusters(GVJ_t * job, Agraph_t * g, int flags) char *color, *fillcolor, *pencolor, **style; node_t *n; edge_t *e; - int oldstate; - oldstate = job->gvc->emit_state; - job->gvc->emit_state = EMIT_CDRAW; for (c = 1; c <= GD_n_cluster(g); c++) { sg = GD_clust(g)[c]; if (clust_in_layer(job, sg) == FALSE) @@ -1417,7 +2303,7 @@ void emit_clusters(GVJ_t * job, Agraph_t * g, int flags) /* when mapping, detect events on clusters after sub_clusters */ if (flags & EMIT_CLUSTERS_LAST) emit_clusters(job, sg, flags); - gvrender_begin_cluster(job, sg); + emit_begin_cluster(job, sg); setColorScheme (agget (sg, "colorscheme")); gvrender_begin_context(job); filled = FALSE; @@ -1499,12 +2385,11 @@ void emit_clusters(GVJ_t * job, Agraph_t * g, int flags) } } gvrender_end_context(job); - gvrender_end_cluster(job, g); + emit_end_cluster(job, g); /* when drawing, lay down clusters before sub_clusters */ if (!(flags & EMIT_CLUSTERS_LAST)) emit_clusters(job, sg, flags); } - job->gvc->emit_state = oldstate; } static bool is_style_delim(int c) @@ -1653,7 +2538,6 @@ static void emit_job(GVJ_t * job, graph_t * g) init_job_viewport(job, g); init_job_pagination(job, g); - job->gvc->emit_state = EMIT_GDRAW; gvrender_begin_job(job); switch (job->output_lang) { diff --git a/lib/common/geom.c b/lib/common/geom.c index dadcc51f9..852f7f227 100644 --- a/lib/common/geom.c +++ b/lib/common/geom.c @@ -356,6 +356,14 @@ int lineToBox(pointf p1, pointf p2, boxf b) return -1; } +void rect2poly(pointf *p) +{ + p[3].x = p[2].x = p[1].x; + p[2].y = p[1].y; + p[3].y = p[0].y; + p[1].x = p[0].x; +} + static pointf rotatepf(pointf p, int cwrot) { static double sina, cosa; diff --git a/lib/common/geomprocs.h b/lib/common/geomprocs.h index df2c03bb1..f48a1872c 100644 --- a/lib/common/geomprocs.h +++ b/lib/common/geomprocs.h @@ -69,6 +69,8 @@ extern pointf ccwrotatepf(pointf p, int ccwrot); extern point cwrotatep(point p, int cwrot); extern pointf cwrotatepf(pointf p, int cwrot); +extern void rect2poly(pointf *p); + #ifdef __cplusplus } #endif diff --git a/lib/gvc/gvcjob.h b/lib/gvc/gvcjob.h index 3f7f029d6..f711e71ef 100644 --- a/lib/gvc/gvcjob.h +++ b/lib/gvc/gvcjob.h @@ -135,15 +135,21 @@ extern "C" { typedef enum {MAP_RECTANGLE, MAP_CIRCLE, MAP_POLYGON, } map_shape_t; + typedef enum {ROOTGRAPH_OBJTYPE, CLUSTER_OBJTYPE, NODE_OBJTYPE, EDGE_OBJTYPE} obj_type; typedef struct obj_state_s obj_state_t; struct obj_state_s { obj_state_t *parent; - graph_t *g; /* this object - only one of *g, *sg, *n, *e should be non-NULL */ - graph_t *sg; - node_t *n; - edge_t *e; + obj_type type; + union { + graph_t *g; + graph_t *sg; + node_t *n; + edge_t *e; + } u; + + int oldstate; /* FIXME - used by one of those other state stacks */ double z, tail_z, head_z; /* z depths for 2.5D renderers such as vrml */ diff --git a/lib/gvc/gvcproc.h b/lib/gvc/gvcproc.h index df77ee7b1..a4ae709cf 100644 --- a/lib/gvc/gvcproc.h +++ b/lib/gvc/gvcproc.h @@ -75,6 +75,9 @@ extern "C" { /* render */ + extern pointf gvrender_ptf(GVJ_t *job, pointf p); + extern pointf* gvrender_ptf_A(GVJ_t *job, pointf *af, pointf *AF, int n); + extern void gvrender_begin_job(GVJ_t * job); extern void gvrender_end_job(GVJ_t * job); extern int gvrender_select(GVJ_t * job, char *lang); diff --git a/lib/gvc/gvrender.c b/lib/gvc/gvrender.c index d8d15038a..2b5b3ac7a 100644 --- a/lib/gvc/gvrender.c +++ b/lib/gvc/gvrender.c @@ -29,18 +29,12 @@ #include "const.h" #include "macros.h" #include "gvplugin_render.h" -#include "globals.h" #include "graph.h" #include "gvcint.h" #include "colorprocs.h" +#include "geom.h" +#include "geomprocs.h" #include "gvcproc.h" -#include "htmltable.h" - -#include "render.h" - -#define FUZZ 3 - -#define P2RECT(p, pr, sx, sy) (pr[0].x = p.x - sx, pr[0].y = p.y - sy, pr[1].x = p.x + sx, pr[1].y = p.y + sy) extern int emit_once(char *str); @@ -121,124 +115,6 @@ int gvrender_features(GVJ_t * job) return features; } -static obj_state_t* push_obj_state(GVJ_t *job) -{ - obj_state_t *obj; - - if (! (obj = zmalloc(sizeof(obj_state_t)))) - agerr(AGERR, "no memory from zmalloc()\n"); - - obj->parent = job->obj; - job->obj = obj; - - return obj; -} - -static void pop_obj_state(GVJ_t *job) -{ - obj_state_t *obj = job->obj; - - assert(obj); - - if (obj->url) free(obj->url); - if (obj->tailurl) free(obj->tailurl); - if (obj->headurl) free(obj->headurl); - if (obj->tooltip) free(obj->tooltip); - if (obj->tailtooltip) free(obj->tailtooltip); - if (obj->headtooltip) free(obj->headtooltip); - if (obj->target) free(obj->target); - if (obj->tailtarget) free(obj->tailtarget); - if (obj->headtarget) free(obj->headtarget); - if (obj->url_map_p) free(obj->url_map_p); - if (obj->url_bsplinemap_p) free(obj->url_bsplinemap_p); - if (obj->url_bsplinemap_n) free(obj->url_bsplinemap_n); - if (obj->tailurl_map_p) free(obj->tailurl_map_p); - if (obj->headurl_map_p) free(obj->headurl_map_p); - - job->obj = obj->parent; - free(obj); -} - -static void doHTMLdata(htmldata_t * dp, point p, void *obj) -{ - char *url = NULL, *target = NULL, *title = NULL; - pointf p1, p2; - int havetitle = 0; - - if ((url = dp->href) && url[0]) { - switch (agobjkind(obj)) { - case AGGRAPH: - url = strdup_and_subst_graph(url, (graph_t *) obj); - break; - case AGNODE: - url = strdup_and_subst_node(url, (node_t *) obj); - break; - case AGEDGE: - url = strdup_and_subst_edge(url, (edge_t *) obj); - break; - } - } - target = dp->target; - if ((title = dp->title) && title[0]) { - havetitle++; - switch (agobjkind(obj)) { - case AGGRAPH: - title = strdup_and_subst_graph(title, (graph_t *) obj); - break; - case AGNODE: - title = strdup_and_subst_node(title, (node_t *) obj); - break; - case AGEDGE: - title = strdup_and_subst_edge(title, (edge_t *) obj); - break; - } - } - if (url || title) { - p1.x = p.x + dp->box.LL.x; - p1.y = p.y + dp->box.LL.y; - p2.x = p.x + dp->box.UR.x; - p2.y = p.y + dp->box.UR.y; -// FIXME -// map_output_rect(p1, p2, url, target, "", title); - } - free(url); - free(title); -} - -/* forward declaration */ -static void doHTMLcell(htmlcell_t * cp, point p, void *obj); - -static void doHTMLtbl(htmltbl_t * tbl, point p, void *obj) -{ - htmlcell_t **cells = tbl->u.n.cells; - htmlcell_t *cp; - - while ((cp = *cells++)) - doHTMLcell(cp, p, obj); - if (tbl->data.href) - doHTMLdata(&tbl->data, p, obj); -} - -static void doHTMLcell(htmlcell_t * cp, point p, void *obj) -{ - if (cp->child.kind == HTML_TBL) - doHTMLtbl(cp->child.u.tbl, p, obj); - if (cp->data.href) - doHTMLdata(&cp->data, p, obj); -} - -static void doHTMLlabel(htmllabel_t * lbl, point p, void *obj) -{ - if (lbl->kind == HTML_TBL) { - doHTMLtbl(lbl->u.tbl, p, obj); - } -} - -/* isRect: - * * isRect function returns true when polygon has - * * regular rectangular shape. Rectangle is regular when - * * it is not skewed and distorted and orientation is almost zero - * */ void gvrender_begin_job(GVJ_t * job) { GVC_t *gvc = job->gvc; @@ -286,7 +162,7 @@ void gvrender_end_job(GVJ_t * job) #define BOLD 1 #define ITALIC 2 -static pointf gvrender_ptf(GVJ_t *job, pointf p) +pointf gvrender_ptf(GVJ_t *job, pointf p) { pointf rv, translation = job->translation, scale = job->compscale; @@ -303,7 +179,7 @@ static pointf gvrender_ptf(GVJ_t *job, pointf p) /* transform an array of n points */ /* *AF and *af must be preallocated */ /* *AF can be the same as *af for inplace transforms */ -static pointf* gvrender_ptf_A(GVJ_t *job, pointf *af, pointf *AF, int n) +pointf* gvrender_ptf_A(GVJ_t *job, pointf *af, pointf *AF, int n) { int i; pointf translation = job->translation, scale = job->compscale; @@ -330,14 +206,6 @@ static int gvrender_comparestr(const void *s1, const void *s2) return strcmp(*(char **) s1, *(char **) s2); } -static void rect2poly(pointf *p) -{ - p[3].x = p[2].x = p[1].x; - p[2].y = p[1].y; - p[3].y = p[0].y; - p[1].x = p[0].x; -} - static void gvrender_resolve_color(gvrender_features_t * features, char *name, gvcolor_t * color) { @@ -369,44 +237,9 @@ static void gvrender_resolve_color(gvrender_features_t * features, void gvrender_begin_graph(GVJ_t * job, graph_t * g) { GVC_t *gvc = job->gvc; - int flags = job->flags; - obj_state_t *obj; gvrender_engine_t *gvre = job->render.engine; - textlabel_t *lab; char *s; - obj = push_obj_state(job); - obj->g = g; - if ((flags & GVRENDER_DOES_LABELS) && ((lab = GD_label(g)))) { - if (lab->html) - doHTMLlabel(lab->u.html, lab->p, (void *) g); - obj->label = lab->text; - } - if ((flags & GVRENDER_DOES_MAPS) - && (((s = agget(g, "href")) && s[0]) - || ((s = agget(g, "URL")) && s[0]))) { - obj->url = strdup_and_subst_graph(s, g); - } - if (flags & GVRENDER_DOES_TOOLTIPS) { - if ((s = agget(g, "tooltip")) && s[0]) { - obj->tooltip = strdup_and_subst_graph(s, g); - obj->explicit_tooltip = true; - } - else if (obj->url && obj->label) { - obj->tooltip = strdup(obj->label); - } - } - if ((flags & GVRENDER_DOES_TARGETS) && ((s = agget(g, "target")) && s[0])) { - obj->target = strdup_and_subst_graph(s, g); - } - - /* init stack */ - gvc->SP = 0; - job->style = &(gvc->styles[0]); - job->style->pen = PEN_SOLID; - job->style->fill = FILL_NONE; - job->style->penwidth = PENWIDTH_NORMAL; - if (gvre) { /* render specific init */ if (gvre->begin_graph) @@ -449,7 +282,6 @@ void gvrender_end_graph(GVJ_t * job) cg->end_graph(); } #endif - pop_obj_state(job); } void gvrender_begin_page(GVJ_t * job) @@ -567,58 +399,7 @@ void gvrender_end_layer(GVJ_t * job) void gvrender_begin_cluster(GVJ_t * job, graph_t * sg) { gvrender_engine_t *gvre = job->render.engine; - obj_state_t *obj; - textlabel_t *lab; - char *s; - int flags, nump = 0; - pointf *p = NULL; - - flags = job->flags; - obj = push_obj_state(job); - obj->sg = sg; - if ((flags & GVRENDER_DOES_LABELS) && ((lab = GD_label(sg)))) { - if (lab->html) - doHTMLlabel(lab->u.html, lab->p, (void *) sg); - obj->label = lab->text; - } - if ((flags & GVRENDER_DOES_MAPS) - && (((s = agget(sg, "href")) && s[0]) || ((s = agget(sg, "URL")) && s[0]))) - obj->url = strdup_and_subst_graph(s, sg); - - if ((flags & GVRENDER_DOES_TARGETS) && ((s = agget(sg, "target")) && s[0])) - obj->target = strdup_and_subst_graph(s, sg); - - if (flags & GVRENDER_DOES_TOOLTIPS) { - if ((s = agget(sg, "tooltip")) && s[0]) { - obj->tooltip = strdup_and_subst_graph(s, sg); - obj->explicit_tooltip = true; - } - else if (obj->label) { - obj->tooltip = strdup(obj->label); - } - } - - if (flags & (GVRENDER_DOES_MAPS | GVRENDER_DOES_TOOLTIPS)) { - if (flags & (GVRENDER_DOES_MAP_RECTANGLE | GVRENDER_DOES_MAP_POLYGON)) { - if (flags & GVRENDER_DOES_MAP_RECTANGLE) { - obj->url_map_shape = MAP_RECTANGLE; - nump = 2; - } - else { - obj->url_map_shape = MAP_POLYGON; - nump = 4; - } - - p = N_NEW(nump, pointf); - P2PF(GD_bb(sg).LL, p[0]); - P2PF(GD_bb(sg).UR, p[1]); - - if (! (flags & (GVRENDER_DOES_MAP_RECTANGLE))) - rect2poly(p); - } - obj->url_map_p = p; - obj->url_map_n = nump; - } + obj_state_t *obj = job->obj; if (gvre) { if (gvre->begin_anchor && (obj->url || obj->explicit_tooltip)) @@ -630,7 +411,6 @@ void gvrender_begin_cluster(GVJ_t * job, graph_t * sg) else { codegen_t *cg = job->codegen; - Obj = CLST; if (cg && cg->begin_anchor && (obj->url || obj->explicit_tooltip)) cg->begin_anchor(obj->url, obj->tooltip, obj->target); if (cg && cg->begin_cluster) @@ -659,9 +439,7 @@ void gvrender_end_cluster(GVJ_t * job, graph_t *g) if (cg && cg->end_anchor && (obj->url || obj->explicit_tooltip)) cg->end_anchor(); } - Obj = NONE; #endif - pop_obj_state(job); } void gvrender_begin_nodes(GVJ_t * job) @@ -736,211 +514,10 @@ void gvrender_end_edges(GVJ_t * job) #endif } -static bool isRect(polygon_t * p) -{ - return (p->sides == 4 && (ROUND(p->orientation) % 90) == 0 - && p->distortion == 0.0 && p->skew == 0.0); -} - -/* - * isFilled function returns 1 if filled style has been set for node 'n' - * otherwise returns 0. it accepts pointer to node_t as an argument - */ -static int ifFilled(node_t * n) -{ - char *style, *p, **pp; - int r = 0; - style = late_nnstring(n, N_style, ""); - if (style[0]) { - pp = parse_style(style); - while ((p = *pp)) { - if (strcmp(p, "filled") == 0) - r = 1; - pp++; - } - } - return r; -} - -/* pEllipse: - * pEllipse function returns 'np' points from the circumference - * of ellipse described by radii 'a' and 'b'. - * Assumes 'np' is greater than zero. - * 'np' should be at least 4 to sample polygon from ellipse - */ -static pointf *pEllipse(double a, double b, int np) -{ - double theta = 0.0; - double deltheta = 2 * M_PI / np; - int i; - pointf *ps; - - ps = N_NEW(np, pointf); - for (i = 0; i < np; i++) { - ps[i].x = a * cos(theta); - ps[i].y = b * sin(theta); - theta += deltheta; - } - return ps; -} - - void gvrender_begin_node(GVJ_t * job, node_t * n) { gvrender_engine_t *gvre = job->render.engine; - obj_state_t *obj; - textlabel_t *lab; - int flags, sides, peripheries, i, j, filled = 0, rect = 0, shape, nump = 0; - polygon_t *poly = NULL; - pointf *vertices, ldimen, *p = NULL; - point coord; - char *s; - - flags = job->flags; - obj = push_obj_state(job); - obj->n = n; - - if (flags & GVRENDER_DOES_Z) { - obj->z = late_double(n, N_z, 0.0, -MAXFLOAT); - } - if ((flags & GVRENDER_DOES_LABELS) && ((lab = ND_label(n)))) { - if (lab->html) - doHTMLlabel(lab->u.html, lab->p, (void *) n); - obj->label = lab->text; - } - if ((flags & GVRENDER_DOES_MAPS) - && (((s = agget(n, "href")) && s[0]) || ((s = agget(n, "URL")) && s[0]))) { - obj->url = strdup_and_subst_node(s, n); - } - if (flags & GVRENDER_DOES_TOOLTIPS) { - if ((s = agget(n, "tooltip")) && s[0]) { - obj->tooltip = strdup_and_subst_node(s, n); - obj->explicit_tooltip = true; - } - else { - obj->tooltip = strdup(ND_label(n)->text); - } - } - if ((flags & GVRENDER_DOES_TARGETS) && ((s = agget(n, "target")) && s[0])) { - obj->target = strdup_and_subst_node(s, n); - } - if ((flags & (GVRENDER_DOES_MAPS | GVRENDER_DOES_TOOLTIPS)) - && (obj->url || obj->explicit_tooltip)) { - - /* checking shape of node */ - shape = shapeOf(n); - /* node coordinate */ - coord = ND_coord_i(n); - /* checking if filled style has been set for node */ - filled = ifFilled(n); - - if (shape == SH_POLY || shape == SH_POINT) { - poly = (polygon_t *) ND_shape_info(n); - - /* checking if polygon is regular rectangle */ - if (isRect(poly) && (poly->peripheries || filled)) - rect = 1; - } - - /* When node has polygon shape and requested output supports polygons - * we use a polygon to map the clickable region that is a: - * circle, ellipse, polygon with n side, or point. - * For regular rectangular shape we have use node's bounding box to map clickable region - */ - if (poly && !rect && (flags & GVRENDER_DOES_MAP_POLYGON)) { - - if (poly->sides < 3) - sides = 1; - else - sides = poly->sides; - - if (poly->peripheries < 2) - peripheries = 1; - else - peripheries = poly->peripheries; - - vertices = poly->vertices; - - if ((s = agget(n, "samplepoints"))) - nump = atoi(s); - /* We want at least 4 points. For server-side maps, at most 100 - * points are allowed. To simplify things to fit with the 120 points - * used for skewed ellipses, we set the bound at 60. - */ - if ((nump < 4) || (nump > 60)) - nump = DFLT_SAMPLE; - /* use bounding box of text label for mapping - * when polygon has no peripheries and node is not filled - */ - if (poly->peripheries == 0 && !filled) { - obj->url_map_shape = MAP_RECTANGLE; - nump = 2; - p = N_NEW(nump, pointf); - ldimen = ND_label(n)->dimen; - P2RECT(coord, p, ldimen.x / 2.0, ldimen.y / 2.0); - } - /* circle or ellipse */ - else if (poly->sides < 3 && poly->skew == 0.0 && poly->distortion == 0.0) { - if (poly->regular) { - obj->url_map_shape = MAP_CIRCLE; - nump = 2; /* center of circle and top right corner of bb */ - p = N_NEW(nump, pointf); - p[0].x = coord.x; - p[0].y = coord.y; - p[1].x = coord.x + vertices[peripheries - 1].x; - p[1].y = coord.y + vertices[peripheries - 1].y; - } - else { /* ellipse is treated as polygon */ - obj->url_map_shape= MAP_POLYGON; - p = pEllipse((double)(vertices[peripheries - 1].x), - (double)(vertices[peripheries - 1].y), nump); - for (i = 0; i < nump; i++) { - p[i].x += coord.x; - p[i].y += coord.y; - } - } - } - /* all other polygonal shape */ - else { - int offset = (peripheries - 1)*(poly->sides); - obj->url_map_shape = MAP_POLYGON; - /* distorted or skewed ellipses and circles are polygons with 120 - * sides. For mapping we convert them into polygon with sample sides - */ - if (poly->sides >= nump) { - int delta = poly->sides / nump; - p = N_NEW(nump, pointf); - for (i = 0, j = 0; j < nump; i += delta, j++) { - p[j].x = coord.x + vertices[i + offset].x; - p[j].y = coord.y + vertices[i + offset].y; - } - } else { - nump = sides; - p = N_NEW(nump, pointf); - for (i = 0; i < nump; i++) { - p[i].x = coord.x + vertices[i + offset].x; - p[i].y = coord.y + vertices[i + offset].y; - } - } - } - } - else { - /* we have to use the node's bounding box to map clickable region - * when requested output format is not capable of polygons. - */ - obj->url_map_shape = MAP_RECTANGLE; - nump = 2; - p = N_NEW(nump, pointf); - p[0].x = coord.x - ND_lw_i(n); - p[0].y = coord.y - (ND_ht_i(n) / 2); - p[1].x = coord.x + ND_rw_i(n); - p[1].y = coord.y + (ND_ht_i(n) / 2); - } - if (! (flags & GVRENDER_DOES_TRANSFORM)) - gvrender_ptf_A(job, p, p, nump); - obj->url_map_p = p; - obj->url_map_n = nump; - } + obj_state_t *obj = job->obj; if (gvre) { if (gvre->begin_anchor && (obj->url || obj->explicit_tooltip)) @@ -952,7 +529,6 @@ void gvrender_begin_node(GVJ_t * job, node_t * n) else { codegen_t *cg = job->codegen; - Obj = NODE; if (cg && cg->begin_anchor && (obj->url || obj->explicit_tooltip)) cg->begin_anchor(obj->url, obj->tooltip, obj->target); if (cg && cg->begin_node) @@ -981,404 +557,13 @@ void gvrender_end_node(GVJ_t * job) if (cg && cg->end_anchor && (obj->url || obj->explicit_tooltip)) cg->end_anchor(); } - Obj = NONE; -#endif - pop_obj_state(job); -} - -#define HW 2.0 /* maximum distance away from line, in points */ - -/* check_control_points: - * check_control_points function checks the size of quadrilateral - * formed by four control points - * returns 1 if four points are in line (or close to line) - * else return 0 - */ -static int check_control_points(pointf *cp) -{ - double dis1 = ptToLine2 (cp[0], cp[3], cp[1]); - double dis2 = ptToLine2 (cp[0], cp[3], cp[2]); - if (dis1 < HW*HW && dis2 < HW*HW) - return 1; - else - return 0; -} - -#ifdef DEBUG -static void psmapOutput (point* ps, int n) -{ - int i; - fprintf (stdout, "newpath %d %d moveto\n", ps[0].x, ps[0].y); - for (i=1; i < n; i++) - fprintf (stdout, "%d %d lineto\n", ps[i].x, ps[i].y); - fprintf (stdout, "closepath stroke\n"); -} -#endif - -typedef struct segitem_s { - pointf p; - struct segitem_s* next; -} segitem_t; - -#define MARK_FIRST_SEG(L) ((L)->next = (segitem_t*)1) -#define FIRST_SEG(L) ((L)->next == (segitem_t*)1) -#define INIT_SEG(P,L) {(L)->next = 0; (L)->p = P;} - -static segitem_t* appendSeg (pointf p, segitem_t* lp) -{ - segitem_t* s = GNEW(segitem_t); - INIT_SEG (p, s); - lp->next = s; - return s; -} - -/* map_bspline_poly: - * Output the polygon determined by the n points in p1, followed - * by the n points in p2 in reverse order. Assumes n <= 50. - */ -static void map_bspline_poly(pointf **pbs_p, int **pbs_n, int *pbs_poly_n, int n, pointf* p1, pointf* p2) -{ - int i = 0, nump = 0, last = 2*n-1; - - for ( ; i < *pbs_poly_n; i++) - nump += (*pbs_n)[i]; - - (*pbs_poly_n)++; - *pbs_n = grealloc(*pbs_n, (*pbs_poly_n) * sizeof(int)); - (*pbs_n)[i] = 2*n; - *pbs_p = grealloc(*pbs_p, (nump + 2*n) * sizeof(pointf)); - - for (i = 0; i < n; i++) { - (*pbs_p)[nump+i] = p1[i]; - (*pbs_p)[nump+last-i] = p2[i]; - } -#ifdef DEBUG - psmapOutput (*pbs_p + nump, last+1); #endif } -/* approx_bezier: - * Approximate Bezier by line segments. If the four points are - * almost colinear, as determined by check_control_points, we store - * the segment cp[0]-cp[3]. Otherwise we split the Bezier into 2 - * and recurse. - * Since 2 contiguous segments share an endpoint, we actually store - * the segments as a list of points. - * New points are appended to the list given by lp. The tail of the - * list is returned. - */ -static segitem_t* approx_bezier (pointf *cp, segitem_t* lp) -{ - pointf sub_curves[8]; - - if (check_control_points(cp)) { - if (FIRST_SEG (lp)) INIT_SEG (cp[0], lp); - lp = appendSeg (cp[3], lp); - } - else { - Bezier (cp, 3, 0.5, sub_curves, sub_curves+4); - lp = approx_bezier (sub_curves, lp); - lp = approx_bezier (sub_curves+4, lp); - } - return lp; -} - -/* bisect: - * Return the angle of the bisector between the two rays - * pp-cp and cp-np. The bisector returned is always to the - * left of pp-cp-np. - */ -static double bisect (pointf pp, pointf cp, pointf np) -{ - double ang, theta, phi; - theta = atan2(np.y - cp.y,np.x - cp.x); - phi = atan2(pp.y - cp.y,pp.x - cp.x); - ang = theta - phi; - if (ang > 0) ang -= 2*M_PI; - - return (phi + ang/2.0); -} - -/* mkSegPts: - * Determine polygon points related to 2 segments prv-cur and cur-nxt. - * The points lie on the bisector of the 2 segments, passing through cur, - * and distance HW from cur. The points are stored in p1 and p2. - * If p1 is NULL, we use the normal to cur-nxt. - * If p2 is NULL, we use the normal to prv-cur. - * Assume at least one of prv or nxt is non-NULL. - */ -static void mkSegPts (segitem_t* prv, segitem_t* cur, segitem_t* nxt, - pointf* p1, pointf* p2) -{ - pointf cp, pp, np; - double theta, delx, dely; - pointf p; - - cp = cur->p; - /* if prv or nxt are NULL, use the one given to create a collinear - * prv or nxt. This could be more efficiently done with special case code, - * but this way is more uniform. - */ - if (prv) { - pp = prv->p; - if (nxt) - np = nxt->p; - else { - np.x = 2*cp.x - pp.x; - np.y = 2*cp.y - pp.y; - } - } - else { - np = nxt->p; - pp.x = 2*cp.x - np.x; - pp.y = 2*cp.y - np.y; - } - theta = bisect(pp,cp,np); - delx = HW*cos(theta); - dely = HW*sin(theta); - p.x = cp.x + delx; - p.y = cp.y + dely; - *p1 = p; - p.x = cp.x - delx; - p.y = cp.y - dely; - *p2 = p; -} - -/* map_output_bspline: - * Construct and output a closed polygon approximating the input - * B-spline bp. We do this by first approximating bp by a sequence - * of line segments. We then use the sequence of segments to determine - * the polygon. - * In cmapx, polygons are limited to 100 points, so we output polygons - * in chunks of 100. - */ -static void map_output_bspline (pointf **pbs, int **pbs_n, int *pbs_poly_n, bezier* bp) -{ - segitem_t* segl = GNEW(segitem_t); - segitem_t* segp = segl; - segitem_t* segprev; - segitem_t* segnext; - int nc, j, k, cnt; - pointf pts[4]; - pointf pt1[50], pt2[50]; - - MARK_FIRST_SEG(segl); - nc = (bp->size - 1)/3; /* nc is number of bezier curves */ - for (j = 0; j < nc; j++) { - for (k = 0; k < 4; k++) { - pts[k].x = (double)bp->list[3*j + k].x; - pts[k].y = (double)bp->list[3*j + k].y; - } - segp = approx_bezier (pts, segp); - } - - segp = segl; - segprev = 0; - cnt = 0; - while (segp) { - segnext = segp->next; - mkSegPts (segprev, segp, segnext, pt1+cnt, pt2+cnt); - cnt++; - if ((segnext == NULL) || (cnt == 50)) { - map_bspline_poly (pbs, pbs_n, pbs_poly_n, cnt, pt1, pt2); - pt1[0] = pt1[cnt-1]; - pt2[0] = pt2[cnt-1]; - cnt = 1; - } - segprev = segp; - segp = segnext; - } - - /* free segl */ - while (segl) { - segp = segl->next; - free (segl); - segl = segp; - } -} - void gvrender_begin_edge(GVJ_t * job, edge_t * e) { gvrender_engine_t *gvre = job->render.engine; - obj_state_t *obj; - char *s; - textlabel_t *lab = NULL, *tlab = NULL, *hlab = NULL; - pointf *p = NULL, *pt = NULL, *ph = NULL, *pte = NULL, *phe = NULL, *pbs = NULL; - int flags, i, nump, *pbs_n = NULL, pbs_poly_n = 0; - bezier bz; - - flags = job->flags; - obj = push_obj_state(job); - obj->e = e; - - if (flags & GVRENDER_DOES_Z) { - obj->tail_z= late_double(e->tail, N_z, 0.0, -1000.0); - obj->head_z= late_double(e->head, N_z, 0.0, -MAXFLOAT); - } - - if (flags & GVRENDER_DOES_LABELS) { - if ((lab = ED_label(e))) { - if (lab->html) - doHTMLlabel(lab->u.html, lab->p, (void *) e); - obj->label = lab->text; - } - obj->taillabel = obj->headlabel = obj->label; - if ((tlab = ED_tail_label(e))) { - if (tlab->html) - doHTMLlabel(tlab->u.html, tlab->p, (void *) e); - obj->taillabel = tlab->text; - } - if ((hlab = ED_head_label(e))) { - if (hlab->html) - doHTMLlabel(hlab->u.html, hlab->p, (void *) e); - obj->headlabel = hlab->text; - } - } - - if (flags & GVRENDER_DOES_MAPS) { - if (((s = agget(e, "href")) && s[0]) || ((s = agget(e, "URL")) && s[0])) - obj->url = strdup_and_subst_edge(s, e); - if (((s = agget(e, "tailhref")) && s[0]) || ((s = agget(e, "tailURL")) && s[0])) - obj->tailurl = strdup_and_subst_edge(s, e); - else if (obj->url) - obj->tailurl = strdup(obj->url); - if (((s = agget(e, "headhref")) && s[0]) || ((s = agget(e, "headURL")) && s[0])) - obj->headurl = strdup_and_subst_edge(s, e); - else if (obj->url) - obj->headurl = strdup(obj->url); - } - - if (flags & GVRENDER_DOES_TARGETS) { - if ((s = agget(e, "target")) && s[0]) - obj->target = strdup_and_subst_edge(s, e); - if ((s = agget(e, "tailtarget")) && s[0]) - obj->tailtarget = strdup_and_subst_edge(s, e); - else if (obj->target) - obj->tailtarget = strdup(obj->target); - if ((s = agget(e, "headtarget")) && s[0]) - obj->headtarget = strdup_and_subst_edge(s, e); - else if (obj->target) - obj->headtarget = strdup(obj->target); - } - - if (flags & GVRENDER_DOES_TOOLTIPS) { - if ((s = agget(e, "tooltip")) && s[0]) { - obj->tooltip = strdup_and_subst_edge(s, e); - obj->explicit_tooltip = true; - } - else if (obj->label) - obj->tooltip = strdup(obj->label); - if ((s = agget(e, "tailtooltip")) && s[0]) { - obj->tailtooltip = strdup_and_subst_edge(s, e); - obj->explicit_tailtooltip = true; - } - else if (obj->taillabel) - obj->tailtooltip = strdup(obj->taillabel); - if ((s = agget(e, "headtooltip")) && s[0]) { - obj->headtooltip = strdup_and_subst_edge(s, e); - obj->explicit_headtooltip = true; - } - else if (obj->headlabel) - obj->headtooltip = strdup(obj->headlabel); - } - - if (flags & (GVRENDER_DOES_MAPS | GVRENDER_DOES_TOOLTIPS)) { - if (flags & (GVRENDER_DOES_MAP_RECTANGLE | GVRENDER_DOES_MAP_POLYGON)) { - if (flags & GVRENDER_DOES_MAP_RECTANGLE) { - obj->url_map_shape = MAP_RECTANGLE; - nump = 2; - } - else { /* GVRENDER_DOES_MAP_POLYGON */ - obj->url_map_shape = MAP_POLYGON; - nump = 4; - } - - if (lab && (obj->url || obj->tooltip)) { - obj->url_map_n = nump; - p = N_NEW(nump, pointf); - P2RECT(lab->p, p, lab->dimen.x / 2., lab->dimen.y / 2.); - } - - if (tlab && (obj->tailurl || obj->tailtooltip)) { - obj->tailurl_map_n = nump; - pt = N_NEW(nump, pointf); - P2RECT(tlab->p, pt, tlab->dimen.x / 2., tlab->dimen.y / 2.); - } - - if (hlab && (obj->headurl || obj->headtooltip)) { - obj->headurl_map_n = nump; - ph = N_NEW(nump, pointf); - P2RECT(hlab->p, ph, hlab->dimen.x / 2., hlab->dimen.y / 2.); - } - - /* process intersecion with tail node */ - if (ED_spl(e) && (obj->tailurl || obj->tailtooltip)) { - obj->tailendurl_map_n = nump; - pte = N_NEW(nump, pointf); - bz = ED_spl(e)->list[0]; - if (bz.sflag) { - /* Arrow at start of splines */ - P2RECT(bz.sp, pte, FUZZ, FUZZ); - } else { - /* No arrow at start of splines */ - P2RECT(bz.list[0], pte, FUZZ, FUZZ); - } - } - - /* process intersection with head node */ - if (ED_spl(e) && (obj->headurl || obj->headtooltip)) { - obj->headendurl_map_n = nump; - phe = N_NEW(nump, pointf); - bz = ED_spl(e)->list[ED_spl(e)->size - 1]; - if (bz.eflag) { - /* Arrow at end of splines */ - P2RECT(bz.ep, phe, FUZZ, FUZZ); - } else { - /* No arrow at end of splines */ - P2RECT(bz.list[bz.size - 1], phe, FUZZ, FUZZ); - } - } - - if (ED_spl(e) && (obj->url || obj->tooltip) && (flags & GVRENDER_DOES_MAP_POLYGON)) { - int ns; - splines *spl; - - spl = ED_spl(e); - ns = spl->size; /* number of splines */ - for (i = 0; i < ns; i++) - map_output_bspline (&pbs, &pbs_n, &pbs_poly_n, spl->list+i); - obj->url_bsplinemap_poly_n = pbs_poly_n; - obj->url_bsplinemap_n = pbs_n; - } - - if (! (flags & GVRENDER_DOES_TRANSFORM)) { - if (p) gvrender_ptf_A(job, p, p, 2); - if (pt) gvrender_ptf_A(job, pt, pt, 2); - if (ph) gvrender_ptf_A(job, ph, ph, 2); - if (pte) gvrender_ptf_A(job, pte, pte, 2); - if (phe) gvrender_ptf_A(job, phe, phe, 2); - if (pbs) { - for ( nump = 0, i = 0; i < pbs_poly_n; i++) - nump += pbs_n[i]; - gvrender_ptf_A(job, pbs, pbs, nump); - } - } - if (! (flags & GVRENDER_DOES_MAP_RECTANGLE)) { - if (p) rect2poly(p); - if (pt) rect2poly(pt); - if (ph) rect2poly(ph); - if (pte) rect2poly(pte); - if (phe) rect2poly(phe); - } - - } - obj->url_map_p = p; - obj->tailurl_map_p = pt; - obj->headurl_map_p = ph; - obj->tailendurl_map_p = pte; - obj->headendurl_map_p = phe; - obj->url_bsplinemap_p = pbs; - } + obj_state_t *obj = job->obj; if (gvre) { if (gvre->begin_anchor && (obj->url || obj->explicit_tooltip)) @@ -1390,7 +575,6 @@ void gvrender_begin_edge(GVJ_t * job, edge_t * e) else { codegen_t *cg = job->codegen; - Obj = EDGE; if (cg && cg->begin_anchor && (obj->url || obj->explicit_tooltip)) cg->begin_anchor(obj->url, obj->tooltip, obj->target); if (cg && cg->begin_edge) @@ -1419,9 +603,7 @@ void gvrender_end_edge(GVJ_t * job) if (cg && cg->end_anchor && (obj->url || obj->explicit_tooltip)) cg->end_anchor(); } - Obj = NONE; #endif - pop_obj_state(job); } void gvrender_begin_context(GVJ_t * job) diff --git a/plugin/core/gvloadimage_core.c b/plugin/core/gvloadimage_core.c index 3f236375f..0b8ced661 100644 --- a/plugin/core/gvloadimage_core.c +++ b/plugin/core/gvloadimage_core.c @@ -132,7 +132,7 @@ static void core_loadimage_vrml(GVJ_t * job, usershape_t *us, boxf b, bool fille assert(us->name); assert(us->f); - n = job->obj->n; + n = job->obj->u.n; assert(n); fprintf(out, "Shape {\n"); diff --git a/plugin/core/gvrender_core_fig.c b/plugin/core/gvrender_core_fig.c index ab12b1950..54013c6fe 100644 --- a/plugin/core/gvrender_core_fig.c +++ b/plugin/core/gvrender_core_fig.c @@ -232,7 +232,7 @@ static void figgen_begin_graph(GVJ_t * job) figgen_printf(job, "# Generated by %s version %s (%s)\n", job->common->info[0], job->common->info[1], job->common->info[2]); figgen_printf(job, "# For: %s\n", job->common->user); - figgen_printf(job, "# Title: %s\n", obj->g->name); + figgen_printf(job, "# Title: %s\n", obj->u.g->name); figgen_printf(job, "# Pages: %d\n", job->pagesArraySize.x * job->pagesArraySize.y); figgen_fputs(job, "Portrait\n"); /* orientation */ figgen_fputs(job, "Center\n"); /* justification */ diff --git a/plugin/core/gvrender_core_map.c b/plugin/core/gvrender_core_map.c index ef034a536..37942d60e 100644 --- a/plugin/core/gvrender_core_map.c +++ b/plugin/core/gvrender_core_map.c @@ -140,7 +140,7 @@ static void map_output_shape (GVJ_t *job, map_shape_t map_shape, pointf * AF, in static void map_begin_page(GVJ_t * job) { obj_state_t *obj = job->obj; - char *name = xml_string(obj->g->name); + char *name = xml_string(obj->u.g->name); switch (job->render.id) { case FORMAT_IMAP: @@ -150,7 +150,7 @@ static void map_begin_page(GVJ_t * job) break; case FORMAT_ISMAP: if (obj->url && obj->url[0]) - fprintf(job->output_file, "default %s %s\n", obj->url, obj->g->name); + fprintf(job->output_file, "default %s %s\n", obj->url, obj->u.g->name); break; case FORMAT_CMAPX: fprintf(job->output_file, "\n", name, name); @@ -183,7 +183,7 @@ static void map_begin_cluster(GVJ_t * job) { obj_state_t *obj = job->obj; - fprintf(job->output_file, "%% %s\n", obj->sg->name); + fprintf(job->output_file, "%% %s\n", obj->u.sg->name); map_output_shape(job, obj->url_map_shape, obj->url_map_p, obj->url_map_n, obj->url, obj->tooltip, obj->target); diff --git a/plugin/core/gvrender_core_ps.c b/plugin/core/gvrender_core_ps.c index cae5794fa..3678ee420 100644 --- a/plugin/core/gvrender_core_ps.c +++ b/plugin/core/gvrender_core_ps.c @@ -90,7 +90,7 @@ static void psgen_begin_graph(GVJ_t * job) setupLatin1 = FALSE; if (job->common->viewNum == 0) { - fprintf(job->output_file, "%%%%Title: %s\n", obj->g->name); + fprintf(job->output_file, "%%%%Title: %s\n", obj->u.g->name); fprintf(job->output_file, "%%%%Pages: (atend)\n"); if (job->common->show_boxes == NULL) fprintf(job->output_file, "%%%%BoundingBox: (atend)\n"); @@ -98,7 +98,7 @@ static void psgen_begin_graph(GVJ_t * job) cat_preamble(job, job->common->lib); epsf_define(job->output_file); } - isLatin1 = (GD_charset(obj->g) == CHAR_LATIN1); + isLatin1 = (GD_charset(obj->u.g) == CHAR_LATIN1); if (isLatin1 && !setupLatin1) { fprintf(job->output_file, "setupLatin1\n"); /* as defined in ps header */ setupLatin1 = TRUE; @@ -168,7 +168,7 @@ static void psgen_begin_cluster(GVJ_t * job) { obj_state_t *obj = job->obj; - fprintf(job->output_file, "%% %s\n", obj->sg->name); + fprintf(job->output_file, "%% %s\n", obj->u.sg->name); if (obj->url && obj->url_map_p) { fprintf(job->output_file, "[ /Rect [ %g %g %g %g ]\n", @@ -287,21 +287,26 @@ static void ps_set_color(GVJ_t *job, gvcolor_t *color) char *objtype; if (color) { - if (job->obj->g) - objtype = "graph"; - else if (job->obj->sg) - objtype = "graph"; - else if (job->obj->n) - objtype = "node"; - else if (job->obj->e) - objtype = "edge"; - else - objtype = "sethsb"; + switch (job->obj->type) { + case ROOTGRAPH_OBJTYPE: + case CLUSTER_OBJTYPE: + objtype = "graph"; + break; + case NODE_OBJTYPE: + objtype = "node"; + break; + case EDGE_OBJTYPE: + objtype = "edge"; + break; + default: + objtype = "sethsb"; + break; + } if ( last_color.u.HSVA[0] != color->u.HSVA[0] || last_color.u.HSVA[1] != color->u.HSVA[1] || last_color.u.HSVA[2] != color->u.HSVA[2] || last_color.u.HSVA[3] != color->u.HSVA[3] - || (job->obj->g && (job->obj->g == job->obj->g->root))) { + || (job->obj->type == ROOTGRAPH_OBJTYPE)) { fprintf(job->output_file, "%.3f %.3f %.3f %scolor\n", color->u.HSVA[0], color->u.HSVA[1], color->u.HSVA[2], objtype); last_color.u.HSVA[0] = color->u.HSVA[0]; diff --git a/plugin/core/gvrender_core_svg.c b/plugin/core/gvrender_core_svg.c index d75ee745c..563ceb0f4 100644 --- a/plugin/core/gvrender_core_svg.c +++ b/plugin/core/gvrender_core_svg.c @@ -212,9 +212,9 @@ static void svggen_begin_graph(GVJ_t * job) obj_state_t *obj = job->obj; svggen_fputs(job, "\n", job->pagesArraySize.x * job->pagesArraySize.y); @@ -267,9 +267,9 @@ static void svggen_begin_page(GVJ_t * job) job->scale.x, job->scale.y, -job->rotation, job->translation.x, -job->translation.y); /* default style */ - if (obj->g->name[0]) { + if (obj->u.g->name[0]) { svggen_fputs(job, ""); - svggen_fputs(job, xml_string(obj->g->name)); + svggen_fputs(job, xml_string(obj->u.g->name)); svggen_fputs(job, "\n"); } } @@ -284,9 +284,9 @@ static void svggen_begin_cluster(GVJ_t * job) obj_state_t *obj = job->obj; svggen_printf(job, "", - obj->sg->meta_node->id); + obj->u.sg->meta_node->id); svggen_fputs(job, ""); - svggen_fputs(job, xml_string(obj->sg->name)); + svggen_fputs(job, xml_string(obj->u.sg->name)); svggen_fputs(job, "\n"); } @@ -299,9 +299,9 @@ static void svggen_begin_node(GVJ_t * job) { obj_state_t *obj = job->obj; - svggen_printf(job, "", obj->n->id); + svggen_printf(job, "", obj->u.n->id); svggen_fputs(job, ""); - svggen_fputs(job, xml_string(obj->n->name)); + svggen_fputs(job, xml_string(obj->u.n->name)); svggen_fputs(job, "\n"); } @@ -316,17 +316,17 @@ svggen_begin_edge(GVJ_t * job) obj_state_t *obj = job->obj; char *edgeop; - svggen_printf(job, "", obj->e->id); - if (obj->e->tail->graph->root->kind & AGFLAG_DIRECTED) + svggen_printf(job, "", obj->u.e->id); + if (obj->u.e->tail->graph->root->kind & AGFLAG_DIRECTED) edgeop = "->"; else edgeop = "--"; svggen_fputs(job, ""); - svggen_fputs(job, xml_string(obj->e->tail->name)); + svggen_fputs(job, xml_string(obj->u.e->tail->name)); svggen_fputs(job, edgeop); /* can't do this in single svggen_printf because * xml_string's buffer gets reused. */ - svggen_fputs(job, xml_string(obj->e->head->name)); + svggen_fputs(job, xml_string(obj->u.e->head->name)); svggen_fputs(job, "\n"); } diff --git a/plugin/gd/gvrender_gd_vrml.c b/plugin/gd/gvrender_gd_vrml.c index ce181889d..46d87327c 100644 --- a/plugin/gd/gvrender_gd_vrml.c +++ b/plugin/gd/gvrender_gd_vrml.c @@ -240,7 +240,7 @@ static void vrml_begin_node(GVJ_t *job) { FILE *out = job->output_file; obj_state_t *obj = job->obj; - node_t *n = obj->n; + node_t *n = obj->u.n; double z = obj->z; int width, height; int transparent; @@ -273,7 +273,8 @@ static void vrml_end_node(GVJ_t *job) static void vrml_begin_edge(GVJ_t *job) { FILE *out = job->output_file; - edge_t *e = job->obj->e; + obj_state_t *obj = job->obj; + edge_t *e = obj->u.e; IsSegment = 0; fprintf(out, "# edge %s -> %s\n", e->tail->name, e->head->name); @@ -325,7 +326,7 @@ finishSegment (FILE *out, edge_t *e) static void vrml_end_edge(GVJ_t *job) { if (IsSegment) - finishSegment(job->output_file, job->obj->e); + finishSegment(job->output_file, job->obj->u.e); fprintf(job->output_file, "] }\n"); } @@ -337,7 +338,7 @@ static void vrml_textpara(GVJ_t *job, pointf p, textpara_t * para) int brect[8]; extern gdFontPtr gdFontSmall; - if (! obj->n) + if (! obj->u.n) return; switch (para->just) { @@ -352,7 +353,7 @@ static void vrml_textpara(GVJ_t *job, pointf p, textpara_t * para) break; } - mp = vrml_node_point(job, obj->n, p); + mp = vrml_node_point(job, obj->u.n, p); err = gdImageStringFT(im, brect, color_index(im, job->style->pencolor), @@ -379,7 +380,7 @@ static double interpolate_zcoord(GVJ_t *job, pointf p1, pointf fst, double fstz, pointf snd, double sndz) { obj_state_t *obj = job->obj; - edge_t *e = obj->e; + edge_t *e = obj->u.e; double len, d, rv; if (fstz == sndz) @@ -463,12 +464,12 @@ vrml_bezier(GVJ_t *job, pointf * A, int n, int arrow_at_start, int arrow_at_end, FILE *out = job->output_file; gvstyle_t *style = job->style; obj_state_t *obj = job->obj; - edge_t *e = obj->e; + edge_t *e = obj->u.e; double fstz = obj->tail_z, sndz = obj->head_z; pointf p1, V[4]; int i, j, step; - assert(obj->e); + assert(e); if (straight(A,n)) { doSegment (job, A, ND_coord_i(e->tail),Fstz,ND_coord_i(e->head),Sndz); @@ -514,7 +515,7 @@ static void doArrowhead (GVJ_t *job, pointf * A) FILE *out = job->output_file; gvstyle_t *style = job->style; obj_state_t *obj = job->obj; - edge_t *e = obj->e; + edge_t *e = obj->u.e; double rad, ht, y; pointf p0; /* center of triangle base */ point tp,hp; @@ -561,9 +562,8 @@ static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled) FILE *out = job->output_file; gvstyle_t *style = job->style; obj_state_t *obj = job->obj; - graph_t *g = obj->g; - node_t *n = obj->n; - edge_t *e = obj->e; + node_t *n; + edge_t *e; double z = obj->z; pointf p, mp; gdPoint *points; @@ -571,16 +571,19 @@ static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled) gdImagePtr brush = NULL; double theta; - if (g) { + switch (obj->type) { + case ROOTGRAPH_OBJTYPE: fprintf(out, " Background { skyColor %.3f %.3f %.3f }\n", style->fillcolor.u.rgba[0] / 255., style->fillcolor.u.rgba[1] / 255., style->fillcolor.u.rgba[2] / 255.); Saw_skycolor = TRUE; - } - else if (n) { + break; + case CLUSTER_OBJTYPE: + break; + case NODE_OBJTYPE: + n = obj->u.n; pen = set_penstyle(job, im, brush); - points = N_GNEW(np, gdPoint); for (i = 0; i < np; i++) { mp = vrml_node_point(job, n, A[i]); @@ -617,9 +620,9 @@ static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled) ND_coord_i(n).x, ND_coord_i(n).y, z + .01); fprintf(out, " }\n"); fprintf(out, "}\n"); - - } - else if (e) { + break; + case EDGE_OBJTYPE: + e = obj->u.e; if (np != 3) { static int flag; if (!flag) { @@ -645,7 +648,6 @@ static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled) atan2((A[0].y + A[2].y) / 2.0 - A[1].y, (A[0].x + A[2].x) / 2.0 - A[1].x) + PI / 2.0; - /* this is gruesome, but how else can we get z coord */ if (DIST2(p, ND_coord_i(e->tail)) < DIST2(p, ND_coord_i(e->head))) z = obj->tail_z; @@ -668,6 +670,7 @@ static void vrml_polygon(GVJ_t *job, pointf * A, int np, int filled) fprintf(out, " }\n"); fprintf(out, " ]\n"); fprintf(out, "}\n"); + break; } } @@ -713,8 +716,8 @@ static void vrml_ellipse(GVJ_t * job, pointf * A, int filled) FILE *out = job->output_file; gvstyle_t *style = job->style; obj_state_t *obj = job->obj; - node_t *n = obj->n; - edge_t *e = obj->e; + node_t *n; + edge_t *e; double z = obj->z; double rx, ry; int dx, dy; @@ -726,7 +729,12 @@ static void vrml_ellipse(GVJ_t * job, pointf * A, int filled) rx = A[1].x - A[0].x; ry = A[1].y - A[0].y; - if (n) { + switch (obj->type) { + case ROOTGRAPH_OBJTYPE: + case CLUSTER_OBJTYPE: + break; + case NODE_OBJTYPE: + n = obj->u.n; P2PF(ND_coord_i(n), mp); if (shapeOf(n) == SH_POINT) { @@ -771,8 +779,9 @@ static void vrml_ellipse(GVJ_t * job, pointf * A, int filled) fprintf(out, " }\n"); fprintf(out, " ]\n"); fprintf(out, "}\n"); - } - else if (e) { + break; + case EDGE_OBJTYPE: + e = obj->u.e; /* this is gruesome, but how else can we get z coord */ if (DIST2(A[0], ND_coord_i(e->tail)) < DIST2(A[0], ND_coord_i(e->head))) z = obj->tail_z;