File: patchworkinit.c

package info (click to toggle)
graphviz 14.0.5-2
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 139,388 kB
  • sloc: ansic: 141,938; cpp: 11,957; python: 7,766; makefile: 4,043; yacc: 3,030; xml: 2,972; tcl: 2,495; sh: 1,388; objc: 1,159; java: 560; lex: 423; perl: 243; awk: 156; pascal: 139; php: 58; ruby: 49; cs: 31; sed: 1
file content (156 lines) | stat: -rw-r--r-- 4,204 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*************************************************************************
 * Copyright (c) 2011 AT&T Intellectual Property 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors: Details at https://graphviz.org
 *************************************************************************/

#include    <assert.h>
#include    <common/render.h>
#include    <common/utils.h>
#include    <patchwork/patchwork.h>
#include    <limits.h>
#include    <neatogen/adjust.h>
#include    <pack/pack.h>
#include    <neatogen/neatoprocs.h>
#include    <stdbool.h>
#include    <util/list.h>

/* the following code shamelessly copied from lib/fdpgen/layout.c
and should be extracted and made into a common function */

typedef LIST(graph_t *) clist_t;

/* mkClusters:
 * Attach list of immediate child clusters.
 * NB: By convention, the indexing starts at 1.
 * If pclist is NULL, the graph is the root graph or a cluster
 * If pclist is non-NULL, we are recursively scanning a non-cluster
 * subgraph for cluster children.
 */
static void
mkClusters (graph_t * g, clist_t* pclist, graph_t* parent)
{
    graph_t* subg;
    clist_t  list = {0};
    clist_t* clist;

    if (pclist == NULL) {
        // [0] is empty. The clusters are in [1..cnt].
        LIST_APPEND(&list, NULL);
        clist = &list;
    }
    else
        clist = pclist;

    for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
        if (is_a_cluster(subg)) {
	    agbindrec(subg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
            LIST_APPEND(clist, subg);
            mkClusters(subg, NULL, subg);
        }
        else {
            mkClusters(subg, clist, parent);
        }
    }
    if (pclist == NULL) {
        assert(LIST_SIZE(&list) - 1 <= INT_MAX);
        GD_n_cluster(g) = (int)(LIST_SIZE(&list) - 1);
        if (LIST_SIZE(&list) > 1) {
            LIST_SHRINK_TO_FIT(&list);
            LIST_DETACH(&list, &GD_clust(g), NULL);
        } else {
            LIST_FREE(&list);
        }
    }
}

static void patchwork_init_node(node_t * n)
{
    agset(n,"shape","box");
}

static void patchwork_init_edge(edge_t * e)
{
    agbindrec(e, "Agedgeinfo_t", sizeof(Agnodeinfo_t), true);  // edge custom data
}

static void patchwork_init_node_edge(graph_t * g)
{
    node_t *n;
    edge_t *e;
    int i = 0;
    rdata* alg = gv_calloc(agnnodes(g), sizeof(rdata));

    GD_neato_nlist(g) = gv_calloc(agnnodes(g) + 1, sizeof(node_t*));
    for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
	agbindrec(n, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);  // node custom data
	ND_alg(n) = alg + i;
	GD_neato_nlist(g)[i++] = n;
	patchwork_init_node(n);

	for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
	    patchwork_init_edge(e);
	}
    }
}

static void patchwork_init_graph(graph_t * g)
{
    N_shape = agattr_text(g, AGNODE, "shape","box");
    setEdgeType (g, EDGETYPE_LINE);
    Ndim = GD_ndim(g) = 2;	/* The algorithm only makes sense in 2D */
    mkClusters(g, NULL, g);
    patchwork_init_node_edge(g);
}

/* patchwork_layout:
 * The current version makes no use of edges, neither for a notion of connectivity
 * nor during drawing.
 */
void patchwork_layout(Agraph_t *g)
{
    patchwork_init_graph(g);

    if ((agnnodes(g) == 0) && (GD_n_cluster(g) == 0)) return;

    patchworkLayout (g);

    dotneato_postprocess(g);
}

static void patchwork_cleanup_graph(graph_t * g)
{
    free(GD_neato_nlist(g));
    free(GD_clust(g));
}

void patchwork_cleanup(graph_t * g)
{
    node_t *n;
    edge_t *e;

    n = agfstnode(g);
    if (!n) return;
    free (ND_alg(n));
    for (; n; n = agnxtnode(g, n)) {
	for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
	    gv_cleanup_edge(e);
	}
	gv_cleanup_node(n);
    }
    patchwork_cleanup_graph(g);
}

/**
 * @dir lib/patchwork
 * @brief squarified [treemap](https://en.wikipedia.org/wiki/Treemapping) layout engine, API patchwork/patchwork.h
 * @ingroup engines
 *
 * [Patchwork layout user manual](https://graphviz.org/docs/layouts/patchwork/)
 *
 * Other @ref engines
 */