]> granicus.if.org Git - graphviz/commitdiff
collect together three graphics-state stacks into one place so that the can be unified
authorellson <devnull@localhost>
Mon, 31 Jul 2006 17:46:44 +0000 (17:46 +0000)
committerellson <devnull@localhost>
Mon, 31 Jul 2006 17:46:44 +0000 (17:46 +0000)
12 files changed:
lib/common/emit.c
lib/common/geom.c
lib/common/geomprocs.h
lib/gvc/gvcjob.h
lib/gvc/gvcproc.h
lib/gvc/gvrender.c
plugin/core/gvloadimage_core.c
plugin/core/gvrender_core_fig.c
plugin/core/gvrender_core_map.c
plugin/core/gvrender_core_ps.c
plugin/core/gvrender_core_svg.c
plugin/gd/gvrender_gd_vrml.c

index d5c6d851f2b8bb2d123498cd28683c1f95205af3..6ba83d79dea7ed1fd37d447916c45399a59d5263 100644 (file)
  *  graphics code generator
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
 #include <ctype.h>
 #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) {
index dadcc51f936f7cb108892d041b968d20aaef77e6..852f7f2279c0d0b9b0b2a26ea93c8a67de580ef6 100644 (file)
@@ -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;
index df2c03bb1a62187a5f3b04bd376fa907212ca47d..f48a1872c219e53d6f13ce47707539faa5955b3e 100644 (file)
@@ -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
index 3f7f029d6da4243aa589aa3fc82de07bb3572be2..f711e71efb53250d57bc9b765061b68cf68f9769 100644 (file)
@@ -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 */
 
index df77ee7b19bc0e6fb0e4e1b70920ef7b1173aee8..a4ae709cf8214eae0f41c7862fd73a74545e550b 100644 (file)
@@ -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);
index d8d15038ad1628bb7efb1301c3d513beabfd88a9..2b5b3ac7a4740d1782dd2823e34f5eb52e4bef15 100644 (file)
 #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)
index 3f236375fa702ff8620518d8fa1fe529f43c3a44..0b8ced661660aa4cd5254d0f8a16dd86db1d7dd6 100644 (file)
@@ -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");
index ab12b195019edcdca073e1d47997478489d3a1d1..54013c6fef5d27d7b559d7022a8c0790969e4cfe 100644 (file)
@@ -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 */
index ef034a5369a501f15dfedaa4593b6be2b811603b..37942d60eede2e4c45156aca61798c5c694a1ea4 100644 (file)
@@ -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, "<map id=\"%s\" name=\"%s\">\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);
index cae5794fa830a4114e42522c7074bafa88a7bb75..3678ee420d1aaf34be59289c064baf3b5fcb203c 100644 (file)
@@ -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];
index d75ee745cd5916357e815d7b83399c222fc6bad5..563ceb0f4103677fe5f2f3099d3ae66867715e91 100644 (file)
@@ -212,9 +212,9 @@ static void svggen_begin_graph(GVJ_t * job)
     obj_state_t *obj = job->obj;
 
     svggen_fputs(job, "<!--");
-    if (obj->g->name[0]) {
+    if (obj->u.g->name[0]) {
         svggen_fputs(job, " Title: ");
-       svggen_fputs(job, xml_string(obj->g->name));
+       svggen_fputs(job, xml_string(obj->u.g->name));
     }
     svggen_printf(job, " Pages: %d -->\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, "<title>");
-        svggen_fputs(job, xml_string(obj->g->name));
+        svggen_fputs(job, xml_string(obj->u.g->name));
         svggen_fputs(job, "</title>\n");
     }
 }
@@ -284,9 +284,9 @@ static void svggen_begin_cluster(GVJ_t * job)
     obj_state_t *obj = job->obj;
 
     svggen_printf(job, "<g id=\"cluster%ld\" class=\"cluster\">",
-           obj->sg->meta_node->id);
+           obj->u.sg->meta_node->id);
     svggen_fputs(job, "<title>");
-    svggen_fputs(job, xml_string(obj->sg->name));
+    svggen_fputs(job, xml_string(obj->u.sg->name));
     svggen_fputs(job, "</title>\n");
 }
 
@@ -299,9 +299,9 @@ static void svggen_begin_node(GVJ_t * job)
 {
     obj_state_t *obj = job->obj;
 
-    svggen_printf(job, "<g id=\"node%ld\" class=\"node\">", obj->n->id);
+    svggen_printf(job, "<g id=\"node%ld\" class=\"node\">", obj->u.n->id);
     svggen_fputs(job, "<title>");
-    svggen_fputs(job, xml_string(obj->n->name));
+    svggen_fputs(job, xml_string(obj->u.n->name));
     svggen_fputs(job, "</title>\n");
 }
 
@@ -316,17 +316,17 @@ svggen_begin_edge(GVJ_t * job)
     obj_state_t *obj = job->obj;
     char *edgeop;
 
-    svggen_printf(job, "<g id=\"edge%ld\" class=\"edge\">", obj->e->id);
-    if (obj->e->tail->graph->root->kind & AGFLAG_DIRECTED)
+    svggen_printf(job, "<g id=\"edge%ld\" class=\"edge\">", obj->u.e->id);
+    if (obj->u.e->tail->graph->root->kind & AGFLAG_DIRECTED)
        edgeop = "&#45;&gt;";
     else
        edgeop = "&#45;&#45;";
     svggen_fputs(job, "<title>");
-    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, "</title>\n");
 }
 
index ce181889d85a1f8a85e8a5684f631ad181aaae2e..46d87327c464340b19b975fdbc78205f7174d52b 100644 (file)
@@ -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;