]> granicus.if.org Git - graphviz/commitdiff
Fix bug where a node whose name starts with "cluster" gets lost in JSON output.
authorEmden Gansner <emdenrg@google.com>
Thu, 11 May 2017 18:59:16 +0000 (14:59 -0400)
committerEmden Gansner <emdenrg@google.com>
Thu, 11 May 2017 18:59:16 +0000 (14:59 -0400)
lib/common/const.h
lib/common/macros.h
lib/common/types.h
lib/common/utils.c
lib/common/utils.h
plugin/core/gvrender_core_json.c

index 605dabd6d22e80e08a2ad16c7d453064bda664ad..2c745c2cd5ecf5a77ca1fe11fa57bd9dc7fc1d1a 100644 (file)
 #define LT_RECD        (2 << 1)
 
 /* Flags stored in GD_flags 
- * Bit(s):  0     HAS_CLUST_EDGE
+ * Bit(s):  0     unused
  *          1-3   ET_ 
  *          4     NEW_RANK
  */
index b84f5c417810025c67d0f78afa30954b022c18ea..be1b5efa50abb2810a6e221e5711d6b5b872a595 100644 (file)
 #define hasPos(n)       (ND_pinned(n) > 0)
 #define isFixed(n)      (ND_pinned(n) > P_SET)
 
+#define CL_EDGE_TAG "cl_edge_info"
 #define SET_CLUST_NODE(n) (ND_clustnode(n) = TRUE)
 #define IS_CLUST_NODE(n)  (ND_clustnode(n))
-#define HAS_CLUST_EDGE(g) (GD_flags(g) & 1)
-#define SET_CLUST_EDGE(g) (GD_flags(g) |= 1)
+#define HAS_CLUST_EDGE(g) (aggetrec(g, CL_EDGE_TAG, FALSE))
 #define EDGE_TYPE(g) (GD_flags(g) & (7 << 1))
 
 #ifndef streq
index 0051a30905f1a02a02d59e9675d16bb2eebe2c2c..3a230e6457a9371936b932ac9091c78a3cfdd3bd 100644 (file)
@@ -553,6 +553,7 @@ typedef enum {NATIVEFONTS,PSFONTS,SVGFONTS} fontname_kind;
        textlabel_t *tail_label;
        textlabel_t *xlabel;
        char edge_type;
+       char compound;
        char adjacent;          /* true for flat edge with adjacent nodes */
        char label_ontop;
        unsigned char gui_state; /* Edge state for GUI ops */
@@ -582,6 +583,7 @@ typedef enum {NATIVEFONTS,PSFONTS,SVGFONTS} fontname_kind;
 #define ED_count(e) (((Agedgeinfo_t*)AGDATA(e))->count)
 #define ED_cutvalue(e) (((Agedgeinfo_t*)AGDATA(e))->cutvalue)
 #define ED_edge_type(e) (((Agedgeinfo_t*)AGDATA(e))->edge_type)
+#define ED_compound(e) (((Agedgeinfo_t*)AGDATA(e))->compound)
 #define ED_adjacent(e) (((Agedgeinfo_t*)AGDATA(e))->adjacent)
 #define ED_factor(e) (((Agedgeinfo_t*)AGDATA(e))->factor)
 #define ED_gui_state(e) (((Agedgeinfo_t*)AGDATA(e))->gui_state)
index 410de3fdb520293e417f9cea43e86df415354400..74c94b096a29636f48c5a2d10391e29d241230c0 100644 (file)
@@ -1045,6 +1045,7 @@ static edge_t *cloneEdge(edge_t * e, node_t * ct, node_t * ch)
     edge_t *ce = agedge(g, ct, ch,NULL,1);
     agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), TRUE);
     agcopyattr(e, ce);
+    ED_compound(ce) = TRUE;
 
     return ce;
 }
@@ -1094,11 +1095,12 @@ static item *mapEdge(Dt_t * map, edge_t * e)
  * routing to avoid edge crossings. At present, this is not implemented,
  * so we could use a simpler model in which we create a single cluster
  * node for each cluster used in a cluster edge.
+ *
+ * Return 1 if cluster edge is created.
  */
 #define MAPC(n) (strncmp(agnameof(n),"cluster",7)?NULL:findCluster(cmap,agnameof(n)))
 
-
-static void
+static int
 checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
 {
     graph_t *tg;
@@ -1110,20 +1112,20 @@ checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
     edge_t *ce;
     item *ip;
 
-    if (IS_CLUST_NODE(h)) return;
+    if (IS_CLUST_NODE(h)) return 0;
     tg = MAPC(t);
     hg = MAPC(h);
     if (!tg && !hg)
-       return;
+       return 0;
     if (tg == hg) {
        agerr(AGWARN, "cluster cycle %s -- %s not supported\n", agnameof(t),
              agnameof(t));
-       return;
+       return 0;
     }
     ip = mapEdge(map, e);
     if (ip) {
        cloneEdge(e, ip->t, ip->h);
-       return;
+       return 1;
     }
 
     if (hg) {
@@ -1131,12 +1133,12 @@ checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
            if (agcontains(hg, tg)) {
                agerr(AGWARN, "tail cluster %s inside head cluster %s\n",
                      agnameof(tg), agnameof(hg));
-               return;
+               return 0;
            }
            if (agcontains(tg, hg)) {
                agerr(AGWARN, "head cluster %s inside tail cluster %s\n",
                      agnameof(hg),agnameof(tg));
-               return;
+               return 0;
            }
            cn = clustNode(t, tg, xb, clg);
            cn1 = clustNode(h, hg, xb, clg);
@@ -1146,7 +1148,7 @@ checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
            if (agcontains(hg, t)) {
                agerr(AGWARN, "tail node %s inside head cluster %s\n",
                      agnameof(t), agnameof(hg));
-               return;
+               return 0;
            }
            cn = clustNode(h, hg, xb, clg);
            ce = cloneEdge(e, t, cn);
@@ -1156,23 +1158,40 @@ checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
        if (agcontains(tg, h)) {
            agerr(AGWARN, "head node %s inside tail cluster %s\n", agnameof(h),
                  agnameof(tg));
-           return;
+           return 0;
        }
        cn = clustNode(t, tg, xb, clg);
        ce = cloneEdge(e, cn, h);
        insertEdge(map, t, h, ce);
     }
+    return 1;
+}
+
+typedef struct {
+    Agrec_t hdr;
+    int n_cluster_edges;
+} cl_edge_t;
+
+static int
+num_clust_edges(graph_t * g)
+{
+    cl_edge_t* cl_info = (cl_edge_t*)HAS_CLUST_EDGE(g);
+    if (cl_info)
+       return cl_info->n_cluster_edges;
+    else
+       return 0;
 }
 
 /* processClusterEdges:
  * Look for cluster edges. Replace cluster edge endpoints
  * corresponding to a cluster with special cluster nodes.
  * Delete original nodes.
- * Return 0 if no cluster edges; 1 otherwise.
+ * If cluster edges are found, a cl_edge_t record will be 
+ * attached to the graph, containing the count of such edges.
  */
-int processClusterEdges(graph_t * g)
+void processClusterEdges(graph_t * g)
 {
-    int rv;
+    int num_cl_edges = 0;
     node_t *n;
     node_t *nxt;
     edge_t *e;
@@ -1189,21 +1208,21 @@ int processClusterEdges(graph_t * g)
     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
        if (IS_CLUST_NODE(n)) continue;
        for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
-           checkCompound(e, clg, &xb, map, cmap);
+           num_cl_edges += checkCompound(e, clg, &xb, map, cmap);
        }
     }
     agxbfree(&xb);
-    dtclose(map);
-    rv = agnnodes(clg);
     for (n = agfstnode(clg); n; n = nxt) {
        nxt = agnxtnode(clg, n);
        agdelete(g, n);
     }
     agclose(clg);
-    if (rv)
-       SET_CLUST_EDGE(g);
+    if (num_cl_edges) {
+       cl_edge_t* cl_info;
+       cl_info = agbindrec(g, CL_EDGE_TAG, sizeof(cl_edge_t), FALSE);
+       cl_info->n_cluster_edges = num_cl_edges;
+    }
     dtclose(cmap);
-    return rv;
 }
 
 /* mapN:
@@ -1231,6 +1250,7 @@ static node_t *mapN(node_t * n, graph_t * clg)
        return nn;
     nn = agnode(g, name, 1);
     agbindrec(nn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), TRUE);
+    SET_CLUST_NODE(nn);
 
     /* Set all attributes to default */
     for (sym = agnxtattr(g, AGNODE, NULL); sym;  (sym = agnxtattr(g, AGNODE, sym))) {
@@ -1248,8 +1268,6 @@ static void undoCompound(edge_t * e, graph_t * clg)
     node_t *nhead;
     edge_t* ce;
 
-    if (!(IS_CLUST_NODE(t) || IS_CLUST_NODE(h)))
-       return;
     ntail = mapN(t, clg);
     nhead = mapN(h, clg);
     ce = cloneEdge(e, ntail, nhead);
@@ -1279,14 +1297,24 @@ void undoClusterEdges(graph_t * g)
     node_t *nextn;
     edge_t *e;
     graph_t *clg;
+    edge_t **elist;
+    int ecnt = num_clust_edges(g);
+    int i = 0;
 
+    if (!ecnt) return;
     clg = agsubg(g, "__clusternodes",1);
     agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), TRUE);
+    elist = N_NEW(ecnt, edge_t*);
     for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
        for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
-           undoCompound(e, clg);
+           if (ED_compound(e))
+               elist[i++] = e;
        }
     }
+    assert(i == ecnt);
+    for (i = 0; i < ecnt; i++)
+       undoCompound(elist[i], clg);
+    free (elist);
     for (n = agfstnode(clg); n; n = nextn) { 
        nextn = agnxtnode(clg, n);
        gv_cleanup_node(n);
index ef7d126bc08710dec63620f7a00565aea080615e..242e5d185d7e7de223c883b669678824a8e67d18 100644 (file)
@@ -90,7 +90,7 @@ extern "C" {
 
     extern void get_gradient_points(pointf * A, pointf * G, int n, float angle, int flags);
 
-    extern int processClusterEdges(graph_t * g);
+    extern void processClusterEdges(graph_t * g);
 
     extern char *latin1ToUTF8(char *);
     extern char *htmlEntityUTF8(char *, graph_t* g);
index 8c0bbe4d2e93685f4bda01a960ab799d421e121b..858bbe6be1d7ed7c8b0807135fa6468517823267 100644 (file)
@@ -574,7 +574,7 @@ static int write_nodes(Agraph_t * g, GVJ_t * job, int top, int has_subgs, state_
        indent(job, sp->Level);
     }
     for (; n; n = agnxtnode(g, n)) {
-       if (IS_CLUSTER(n)) continue;
+       if (IS_CLUST_NODE(n)) continue;
        if (not_first) 
             if (top)
                gvputs(job, ",\n");
@@ -672,7 +672,7 @@ static void write_graph(Agraph_t * g, GVJ_t * job, int top, state_t* sp)
        aginit(g, AGRAPH, ID, -((int)sizeof(gvid_t)), FALSE);
        sgcnt = label_subgs(g, sgcnt, map);
        for (np = agfstnode(g); np; np = agnxtnode(g,np)) {
-           if (IS_CLUSTER(np)) {
+           if (IS_CLUST_NODE(np)) {
                ND_gid(np) = lookup(map, agnameof(np));
            }
            else {