]> granicus.if.org Git - graphviz/commitdiff
Allow a graph object to specify any list of layers to which it should belong;
authorEmden Gansner <erg@research.att.com>
Mon, 27 Feb 2012 16:28:24 +0000 (11:28 -0500)
committerEmden Gansner <erg@research.att.com>
Mon, 27 Feb 2012 16:28:24 +0000 (11:28 -0500)
allow a graph to specify a list of layers to be emitted.

doc/info/attrs.html
doc/infosrc/attrs
doc/infosrc/types
lib/common/const.h
lib/common/emit.c
lib/gvc/gvcint.h

index 92adf2eceb31462edaa444558711236f34e8d577..7e805581e5b6db7ba2c41c5940980e1aa572be2a 100644 (file)
@@ -265,10 +265,15 @@ This field indicates which graph component uses the attribute.
 </TD><TD>G</TD><TD><A HREF=#k:bool>bool</A>
 </TD><TD ALIGN="CENTER">false</TD><TD></TD><TD></TD> </TR>
  <TR><TD><A NAME=a:layer HREF=#d:layer>layer</A>
-</TD><TD>EN</TD><TD><A HREF=#k:layerRange>layerRange</A>
+</TD><TD>ENC</TD><TD><A HREF=#k:layerRange>layerRange</A>
 </TD><TD ALIGN="CENTER">""</TD><TD></TD><TD></TD> </TR>
+ <TR><TD><A NAME=a:layerlistsep HREF=#d:layerlistsep>layerlistsep</A>
+</TD><TD>G</TD><TD>string</TD><TD ALIGN="CENTER">","</TD><TD></TD><TD></TD> </TR>
  <TR><TD><A NAME=a:layers HREF=#d:layers>layers</A>
 </TD><TD>G</TD><TD><A HREF=#k:layerList>layerList</A>
+</TD><TD ALIGN="CENTER">""</TD><TD></TD><TD></TD> </TR>
+ <TR><TD><A NAME=a:layerselect HREF=#d:layerselect>layerselect</A>
+</TD><TD>G</TD><TD><A HREF=#k:layerRange>layerRange</A>
 </TD><TD ALIGN="CENTER">""</TD><TD></TD><TD></TD> </TR>
  <TR><TD><A NAME=a:layersep HREF=#d:layersep>layersep</A>
 </TD><TD>G</TD><TD>string</TD><TD ALIGN="CENTER">" :\t"</TD><TD></TD><TD></TD> </TR>
@@ -1105,7 +1110,11 @@ This field indicates which graph component uses the attribute.
   <A HREF=#d:orientation><TT>orientation=landscape</TT></A>.
 
 <DT><A NAME=d:layer HREF=#a:layer><STRONG>layer</STRONG></A>
-<DD>  Specifies layers in which the node or edge is present.
+<DD>  Specifies layers in which the node, edge or cluster is present.
+
+<DT><A NAME=d:layerlistsep HREF=#a:layerlistsep><STRONG>layerlistsep</STRONG></A>
+<DD>  Specifies the separator characters used to split the
+  an attribute of type <A HREF=#k:layerRange>layerRange</A> into a list of ranges.
 
 <DT><A NAME=d:layers HREF=#a:layers><STRONG>layers</STRONG></A>
 <DD>  Specifies a linearly ordered list of layer names attached to the graph
@@ -1113,9 +1122,12 @@ This field indicates which graph component uses the attribute.
   belonging to the current output layer appear. For more information,
   see the page <A HREF="http://www.graphviz.org/wiki/how-use-drawing-layers-overlays">How to use drawing layers (overlays)</A>.
 
+<DT><A NAME=d:layerselect HREF=#a:layerselect><STRONG>layerselect</STRONG></A>
+<DD>  Selects a list of layers to be emitted.
+
 <DT><A NAME=d:layersep HREF=#a:layersep><STRONG>layersep</STRONG></A>
 <DD>  Specifies the separator characters used to split the
-  <A HREF=#d:layers>layers </A>attribute into a list of layer names.
+  <A HREF=#d:layers>layers</A>attribute into a list of layer names.
 
 <DT><A NAME=d:layout HREF=#a:layout><STRONG>layout</STRONG></A>
 <DD>  Specifies the name of the layout algorithm to use, such as "dot"
@@ -2072,11 +2084,20 @@ enclosed in the parentheses,  <TT>(...)+</TT> indicates 1 or more, and
   names and implicitly numbered 1,2,...
 
 <DT><A NAME=k:layerRange><STRONG>layerRange</STRONG></A>
-<DD>layerId or layerId<B>s</B>layerId,<BR> where layerId = "all",
+<DD>specifies a list of layers defined by the <A HREF=#a:layers>layers</A> attribute.
+  It consists of a list of layer intervals separated by any collection of characters from
+  the <A HREF=#a:layerlistsep>layerlistsep</A> attribute.
+  Each layer interval is specified as either a
+  layerId or a layerId<B>s</B>layerId<BR>, where layerId = "all",
   a decimal integer or a <A HREF=#a:layer>layer</A> name.
-  (An integer i corresponds to layer i.)
+  (An integer i corresponds to layer i, layers being numbered from 1.)
   The string <B>s</B> consists of 1 or more separator characters specified
   by the <A HREF=#a:layersep>layersep</A> attribute.
+  <P>
+  Thus, assuming the default values for <A HREF=#a:layersep>layersep</A>
+  and <A HREF=#a:layerlistsep>layerlistsep</A>, if <TT>layers="a:b:c:d:e:f:g:h"</TT>, the
+  layerRange string <TT>layers="a:b,d,f:all"</TT> would denote the layers
+  <TT>a b d f g h"</TT>.
 
 <DT><A NAME=k:lblString><STRONG>lblString</STRONG></A>
 <DD>an <A HREF=#k:escString>escString</A>
index e36461dd598f4d5c9184e90953b5f4492806fd10..755599790211553a48f5968ed3cf128f5eccf5f1 100644 (file)
@@ -586,16 +586,21 @@ removal and straightening.
 If true, the graph is rendered in landscape mode. Synonymous with
 <A HREF=#d:rotate><TT>rotate=90</TT></A> or
 <A HREF=#d:orientation><TT>orientation=landscape</TT></A>.
-:layer:EN:layerRange:"";
-Specifies layers in which the node or edge is present.
+:layer:ENC:layerRange:"";
+Specifies layers in which the node, edge or cluster is present.
 :layers:G:layerList:"";
 Specifies a linearly ordered list of layer names attached to the graph
 The graph is then output in separate layers. Only those components
 belonging to the current output layer appear. For more information,
 see the page <A HREF="http://www.graphviz.org/wiki/how-use-drawing-layers-overlays">How to use drawing layers (overlays)</A>.
+:layerlistsep:G:string:",";
+Specifies the separator characters used to split the
+an attribute of type <A HREF=#k:layerRange>layerRange</A> into a list of ranges.
+:layerselect:G:layerRange:"";
+Selects a list of layers to be emitted.
 :layersep:G:string:" :\\\\t";
 Specifies the separator characters used to split the
-<A HREF=#d:layers>layers </A>attribute into a list of layer names.
+<A HREF=#d:layers>layers</A>attribute into a list of layer names.
 :layout:G:string:"";
 Specifies the name of the layout algorithm to use, such as "dot"
 or "neato". Normally, graphs should be kept independent of a type of
index cee60f20a417dffa31162ba5f7ae913cfbaeca74..fae1f25dd649906a54fb699ac7d35407daade656 100644 (file)
@@ -163,11 +163,20 @@ list of strings separated by characters from the
 tabs or spaces), defining <A HREF=#a:layer>layer</A>
 names and implicitly numbered 1,2,...
 :layerRange
-layerId or layerId<B>s</B>layerId,<BR> where layerId = "all",
+specifies a list of layers defined by the <A HREF=#a:layers>layers</A> attribute.
+It consists of a list of layer intervals separated by any collection of characters from
+the <A HREF=#a:layerlistsep>layerlistsep</A> attribute.
+Each layer interval is specified as either a
+layerId or a layerId<B>s</B>layerId<BR>, where layerId = "all",
 a decimal integer or a <A HREF=#a:layer>layer</A> name.
-(An integer i corresponds to layer i.)
+(An integer i corresponds to layer i, layers being numbered from 1.)
 The string <B>s</B> consists of 1 or more separator characters specified
 by the <A HREF=#a:layersep>layersep</A> attribute.
+<P>
+Thus, assuming the default values for <A HREF=#a:layersep>layersep</A>
+and <A HREF=#a:layerlistsep>layerlistsep</A>, if <TT>layers="a:b:c:d:e:f:g:h"</TT>, the
+layerRange string <TT>layers="a:b,d,f:all"</TT> would denote the layers
+<TT>a b d f g h"</TT>.
 :outputMode
 "breadthfirst","nodesfirst","edgesfirst"
 These specify the order in which nodes and edges are drawn in concrete
index 0c2380ba8a8237e12a0124621494a605831d8969..f6c4ea9487493c6984197337994122181229d3b5 100644 (file)
@@ -77,6 +77,7 @@
 #define                NODENAME_ESC            "\\N"
 
 #define                DEFAULT_LAYERSEP        ":\t "
+#define                DEFAULT_LAYERLISTSEP    ","
 
 #define                DEFAULT_NODESEP 0.25
 #define                MIN_NODESEP             0.02
index 8fc01ee519c892a6b92a662630d24b84b023aa8a..d2ba2314b504f1f1525c3a98b9b87c963dd76af8 100644 (file)
@@ -654,6 +654,118 @@ static void map_output_bspline (pointf **pbs, int **pbs_n, int *pbs_poly_n, bezi
     }
 }
 
+static boolean is_natural_number(char *sstr)
+{
+    unsigned char *str = (unsigned char *) sstr;
+
+    while (*str)
+       if (NOT(isdigit(*str++)))
+           return FALSE;
+    return TRUE;
+}
+
+static int layer_index(GVC_t *gvc, char *str, int all)
+{
+    /* GVJ_t *job = gvc->job; */
+    int i;
+
+    if (streq(str, "all"))
+       return all;
+    if (is_natural_number(str))
+       return atoi(str);
+    if (gvc->layerIDs)
+       for (i = 1; i <= gvc->numLayers; i++)
+           if (streq(str, gvc->layerIDs[i]))
+               return i;
+    return -1;
+}
+
+static boolean selectedLayer(GVC_t *gvc, int layerNum, int numLayers, char *spec)
+{
+    int n0, n1;
+    unsigned char buf[SMALLBUF];
+    char *w0, *w1;
+    char *buf_part_p = NULL, *buf_p = NULL, *cur, *part_in_p;
+    agxbuf xb;
+    boolean rval = FALSE;
+
+    agxbinit(&xb, SMALLBUF, buf);
+    agxbput(&xb, spec);
+    part_in_p = agxbuse(&xb);
+
+    while ((rval == FALSE) && (cur = strtok_r(part_in_p, gvc->layerListDelims, &buf_part_p))) {
+       w1 = w0 = strtok_r (cur, gvc->layerDelims, &buf_p);
+       if (w0)
+           w1 = strtok_r (NULL, gvc->layerDelims, &buf_p);
+       switch ((w0 != NULL) + (w1 != NULL)) {
+       case 0:
+           rval = FALSE;
+           break;
+       case 1:
+           n0 = layer_index(gvc, w0, layerNum);
+           rval = (n0 == layerNum);
+           break;
+       case 2:
+           n0 = layer_index(gvc, w0, 0);
+           n1 = layer_index(gvc, w1, numLayers);
+           if ((n0 >= 0) || (n1 >= 0)) {
+               if (n0 > n1) {
+                   int t = n0;
+                   n0 = n1;
+                   n1 = t;
+               }
+               rval = BETWEEN(n0, layerNum, n1);
+           }
+           break;
+       }
+       part_in_p = NULL;
+    }
+    agxbfree(&xb);
+    return rval;
+}
+
+static boolean selectedlayer(GVJ_t *job, char *spec)
+{
+    return selectedLayer (job->gvc, job->layerNum, job->numLayers, spec);
+}
+
+/* parse_layerselect:
+ * Parse the graph's layerselect attribute, which determines
+ * which layers are emitted. The specification is the same used
+ * by the layer attribute.
+ *
+ * If we find n layers, we return an array arr of n+2 ints. arr[0]=n.
+ * arr[n+1]=numLayers+1, acting as a sentinel. The other entries give
+ * the desired layer indices.
+ *
+ * If no layers are detected, NULL is returned.
+ *
+ * This implementation does a linear walk through each layer index and
+ * uses selectedLayer to match it against p. There is probably a more
+ * efficient way to do this, but this is simple and until we find people
+ * using huge numbers of layers, it should be adequate.
+ */
+static int* parse_layerselect(GVC_t *gvc, graph_t * g, char *p)
+{
+    int* laylist = N_GNEW(gvc->numLayers+2,int);
+    int i, cnt = 0;
+    for (i = 1; i <=gvc->numLayers; i++) {
+       if (selectedLayer (gvc, i, gvc->numLayers, p)) {
+           laylist[++cnt] = i;
+       } 
+    }
+    if (cnt) {
+       laylist[0] = cnt;
+       laylist[cnt+1] = gvc->numLayers+1;
+    }
+    else {
+       agerr(AGWARN, "The layerselect attribute \"%s\" does not match any layer specifed by the layers attribute - ignored.\n", p);
+       laylist[0] = cnt;
+       free (laylist);
+       laylist = NULL;
+    }
+    return laylist;
+}
 
 /* parse_layers:
  * Split input string into tokens, with separators specified by
@@ -671,6 +783,13 @@ static int parse_layers(GVC_t *gvc, graph_t * g, char *p)
     gvc->layerDelims = agget(g, "layersep");
     if (!gvc->layerDelims)
         gvc->layerDelims = DEFAULT_LAYERSEP;
+    gvc->layerListDelims = agget(g, "layerlistsep");
+    if (!gvc->layerListDelims)
+        gvc->layerListDelims = DEFAULT_LAYERLISTSEP;
+    if ((tok = strpbrk (gvc->layerDelims, gvc->layerListDelims))) { /* conflict in delimiter strings */
+       agerr(AGWARN, "The character \'%c\' appears in both the layersep and layerlistsep attributes - layerlistsep ignored.\n", *tok);
+        gvc->layerListDelims = "";
+    }
 
     ntok = 0;
     sz = 0;
@@ -717,32 +836,65 @@ static void init_layering(GVC_t * gvc, graph_t * g)
 
     /* free layer strings and pointers from previous graph */
     if (gvc->layers) {
-               free(gvc->layers);
-               gvc->layers = NULL;
-       }
+       free(gvc->layers);
+       gvc->layers = NULL;
+    }
     if (gvc->layerIDs) {
-               free(gvc->layerIDs);
-               gvc->layerIDs = NULL;
-       }
+       free(gvc->layerIDs);
+       gvc->layerIDs = NULL;
+    }
+    if (gvc->layerlist) {
+       free(gvc->layerlist);
+       gvc->layerlist = NULL;
+    }
     if ((str = agget(g, "layers")) != 0) {
        gvc->numLayers = parse_layers(gvc, g, str);
+       if (((str = agget(g, "layerselect")) != 0) && *str) {
+           gvc->layerlist = parse_layerselect(gvc, g, str);
+       }
     } else {
        gvc->layerIDs = NULL;
        gvc->numLayers = 1;
     }
 }
 
-static void firstlayer(GVJ_t *job)
+/* numPhysicalLayers:
+ * Return number of physical layers to be emitted.
+ */
+static int numPhysicalLayers (GVJ_t *job)
+{
+    if (job->gvc->layerlist) {
+       return job->gvc->layerlist[0];
+    }
+    else
+       return job->numLayers;
+
+}
+
+static void firstlayer(GVJ_t *job, int** listp)
 {
     job->numLayers = job->gvc->numLayers;
-    if ((job->numLayers > 1)
+    if (job->gvc->layerlist) {
+       int *list = job->gvc->layerlist;
+       int cnt = *list++;
+       if ((cnt > 1) && (! (job->flags & GVDEVICE_DOES_LAYERS))) {
+           agerr(AGWARN, "layers not supported in %s output\n",
+               job->output_langname);
+           list[1] = job->numLayers + 1; /* only one layer printed */
+       }
+       job->layerNum = *list++;
+       *listp = list;
+    }
+    else {
+       if ((job->numLayers > 1)
                && (! (job->flags & GVDEVICE_DOES_LAYERS))) {
-       agerr(AGWARN, "layers not supported in %s output\n",
+           agerr(AGWARN, "layers not supported in %s output\n",
                job->output_langname);
-       job->numLayers = 1;
+           job->numLayers = 1;
+       }
+       job->layerNum = 1;
+       *listp = NULL;
     }
-
-    job->layerNum = 1;
 }
 
 static boolean validlayer(GVJ_t *job)
@@ -750,9 +902,15 @@ static boolean validlayer(GVJ_t *job)
     return (job->layerNum <= job->numLayers);
 }
 
-static void nextlayer(GVJ_t *job)
+static void nextlayer(GVJ_t *job, int** listp)
 {
-    job->layerNum++;
+    int *list = *listp;
+    if (list) {
+       job->layerNum = *list++;
+       *listp = list;
+    }
+    else
+       job->layerNum++;
 }
 
 static point pagecode(GVJ_t *job, char c)
@@ -1173,71 +1331,6 @@ fprintf(stderr,"width=%d height=%d dpi=%g,%g\npad=%g,%g focus=%g,%g view=%g,%g z
 #endif
 }
 
-static boolean is_natural_number(char *sstr)
-{
-    unsigned char *str = (unsigned char *) sstr;
-
-    while (*str)
-       if (NOT(isdigit(*str++)))
-           return FALSE;
-    return TRUE;
-}
-
-static int layer_index(GVC_t *gvc, char *str, int all)
-{
-    GVJ_t *job = gvc->job;
-    int i;
-
-    if (streq(str, "all"))
-       return all;
-    if (is_natural_number(str))
-       return atoi(str);
-    if (gvc->layerIDs)
-       for (i = 1; i <= job->numLayers; i++)
-           if (streq(str, gvc->layerIDs[i]))
-               return i;
-    return -1;
-}
-
-static boolean selectedlayer(GVJ_t *job, char *spec)
-{
-    GVC_t *gvc = job->gvc;
-    int n0, n1;
-    unsigned char buf[SMALLBUF];
-    char *w0, *w1;
-    agxbuf xb;
-    boolean rval = FALSE;
-
-    agxbinit(&xb, SMALLBUF, buf);
-    agxbput(&xb, spec);
-    w1 = w0 = strtok(agxbuse(&xb), gvc->layerDelims);
-    if (w0)
-       w1 = strtok(NULL, gvc->layerDelims);
-    switch ((w0 != NULL) + (w1 != NULL)) {
-    case 0:
-       rval = FALSE;
-       break;
-    case 1:
-       n0 = layer_index(gvc, w0, job->layerNum);
-       rval = (n0 == job->layerNum);
-       break;
-    case 2:
-       n0 = layer_index(gvc, w0, 0);
-       n1 = layer_index(gvc, w1, job->numLayers);
-       if ((n0 < 0) || (n1 < 0))
-           rval = TRUE;
-       else if (n0 > n1) {
-           int t = n0;
-           n0 = n1;
-           n1 = t;
-       }
-       rval = BETWEEN(n0, job->layerNum, n1);
-       break;
-    }
-    agxbfree(&xb);
-    return rval;
-}
-
 static boolean node_in_layer(GVJ_t *job, graph_t * g, node_t * n)
 {
     char *pn, *pe;
@@ -3113,7 +3206,7 @@ static void emit_page(GVJ_t * job, graph_t * g)
        emit_map_rect(job, job->clip);
        gvrender_begin_anchor(job, obj->url, obj->tooltip, obj->target, obj->id);
     }
-    if (job->numLayers == 1)
+    if (numPhysicalLayers(job) == 1)
        emit_background(job, g);
     if (GD_label(g))
        emit_label(job, EMIT_GLABEL, GD_label(g));
@@ -3128,6 +3221,7 @@ void emit_graph(GVJ_t * job, graph_t * g)
     node_t *n;
     char *s;
     int flags = job->flags;
+    int* lp;
 
     /* device dpi is now known */
     job->scale.x = job->zoom * job->dpi.x / POINTS_PER_INCH;
@@ -3168,15 +3262,15 @@ fprintf(stderr,"focus=%g,%g view=%g,%g\n",
     for (n = agfstnode(g); n; n = agnxtnode(g, n))
        ND_state(n) = 0;
     /* iterate layers */
-    for (firstlayer(job); validlayer(job); nextlayer(job)) {
-       if (job->numLayers > 1)
+    for (firstlayer(job,&lp); validlayer(job); nextlayer(job,&lp)) {
+       if (numPhysicalLayers (job) > 1)
            gvrender_begin_layer(job);
 
        /* iterate pages */
        for (firstpage(job); validpage(job); nextpage(job))
            emit_page(job, g);
 
-       if (job->numLayers > 1)
+       if (numPhysicalLayers (job) > 1)
            gvrender_end_layer(job);
     } 
     emit_end_graph(job, g);
index 1cd31c1e93b2c55132c22fe1321fd3462ba80c6b..e5632971de587f9269af3720fd9191ac6ebd58d3 100644 (file)
@@ -124,9 +124,11 @@ extern "C" {
 
        /* layers */
        char *layerDelims;      /* delimiters in layer names */
+       char *layerListDelims;  /* delimiters between layer ranges */ 
        char *layers;           /* null delimited list of layer names */
        char **layerIDs;        /* array of layer names */
        int numLayers;          /* number of layers */
+       int *layerlist;
 
        /* default font */
        char *defaultfontname;