Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
clusteredges.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11
12/* clusteredges.c:
13 * Written by Emden R. Gansner
14 *
15 * Code for handling spline edges around clusters.
16 */
17
18/* uses PRIVATE interface */
19#define FDP_PRIVATE 1
20
21#include "config.h"
22#include <assert.h>
23#include <cgraph/alloc.h>
24#include <cgraph/list.h>
25#include <fdpgen/clusteredges.h>
26#include <fdpgen/fdp.h>
27#include <limits.h>
28#include <neatogen/neatoprocs.h>
29#include <pathplan/vispath.h>
30#include <pack/pack.h>
31#include <stdbool.h>
32
33DEFINE_LIST(objlist, Ppoly_t*)
34
35#if defined(DEBUG) && DEBUG > 1
36static void dumpObj(Ppoly_t * p)
37{
38 Ppoint_t pt;
39 for (size_t j = 0; j < p->pn; j++) {
40 pt = p->ps[j];
41 fprintf(stderr, " %.5g %.5g", pt.x, pt.y);
42 }
43 fputs("\n", stderr);
44}
45
46static void dumpObjlist(const objlist_t *l) {
47 for (size_t i = 0; i < objlist_size(l); i++) {
48 dumpObj(objlist_get(l, i));
49 }
50}
51#endif
52
53/* makeClustObs:
54 * Create an obstacle corresponding to a cluster's bbox.
55 */
57{
58 Ppoly_t *obs = gv_alloc(sizeof(Ppoly_t));
59 boxf bb;
60 boxf newbb;
61 Ppoint_t ctr;
62
63 bb = GD_bb(g);
64 obs->pn = 4;
65 obs->ps = gv_calloc(4, sizeof(Ppoint_t));
66
67 ctr.x = (bb.UR.x + bb.LL.x) / 2.0;
68 ctr.y = (bb.UR.y + bb.LL.y) / 2.0;
69
70 if (pm->doAdd) {
71 newbb.UR.x = bb.UR.x + pm->x;
72 newbb.UR.y = bb.UR.y + pm->y;
73 newbb.LL.x = bb.LL.x - pm->x;
74 newbb.LL.y = bb.LL.y - pm->y;
75 }
76 else {
77 double deltax = pm->x - 1.0;
78 double deltay = pm->y - 1.0;
79 newbb.UR.x = pm->x * bb.UR.x - deltax * ctr.x;
80 newbb.UR.y = pm->y * bb.UR.y - deltay * ctr.y;
81 newbb.LL.x = pm->x * bb.LL.x - deltax * ctr.x;
82 newbb.LL.y = pm->y * bb.LL.y - deltay * ctr.y;
83 }
84
85 /* CW order */
86 obs->ps[0].x = newbb.LL.x;
87 obs->ps[0].y = newbb.LL.y;
88 obs->ps[1].x = newbb.LL.x;
89 obs->ps[1].y = newbb.UR.y;
90 obs->ps[2].x = newbb.UR.x;
91 obs->ps[2].y = newbb.UR.y;
92 obs->ps[3].x = newbb.UR.x;
93 obs->ps[3].y = newbb.LL.y;
94
95 return obs;
96}
97
98/* addGraphObjs:
99 * Add all top-level clusters and nodes with g as their smallest
100 * containing graph to the list l.
101 * Don't add any objects equal to tex or hex.
102 * Return the list.
103 */
104static void
105addGraphObjs(objlist_t *l, graph_t *g, void *tex, void *hex, expand_t *pm) {
106 node_t *n;
107 graph_t *sg;
108 int i;
109
110 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
111 if (PARENT(n) == g && n != tex && n != hex && !IS_CLUST_NODE(n)) {
112 objlist_append(l, makeObstacle(n, pm, false));
113 }
114 }
115 for (i = 1; i <= GD_n_cluster(g); i++) {
116 sg = GD_clust(g)[i];
117 if (sg != tex && sg != hex) {
118 objlist_append(l, makeClustObs(sg, pm));
119 }
120 }
121}
122
123/* raiseLevel:
124 * Add barrier objects for node n, in graph *gp of level maxlvl, up to
125 * level minlvl.
126 * Assume maxlvl > minlvl.
127 * Return appended list, plus pass back last cluster processed in gp.
128 */
129static void
130raiseLevel(objlist_t *l, int maxlvl, void *ex, int minlvl, graph_t **gp,
131 expand_t* pm)
132{
133 graph_t *g = *gp;
134 int i;
135
136 for (i = maxlvl; i > minlvl; i--) {
137 addGraphObjs(l, g, ex, NULL, pm);
138 ex = g;
139 g = GPARENT(g);
140 }
141 *gp = ex;
142}
143
144/* objectList:
145 * Create array of all objects (nodes and clusters) to be avoided
146 * when routing edge e. Make sure it never adds the endpoints of the
147 * edge, or any graph containing the endpoints.
148 * Return the list.
149 * Assume e is not a loop.
150 */
151static objlist_t objectList(edge_t *ep, expand_t *pm) {
152 node_t *h = aghead(ep);
153 node_t *t = agtail(ep);
154 graph_t *hg = PARENT(h);
155 graph_t *tg = PARENT(t);
156 int hlevel;
157 int tlevel;
158 void *hex; /* Objects to be excluded from list */
159 void *tex;
160 objlist_t list = {0};
161
162 /* If either endpoint is a cluster node, we move up one level */
163 if (IS_CLUST_NODE(h)) {
164 hex = hg;
165 hg = GPARENT(hg);
166 } else
167 hex = h;
168 if (IS_CLUST_NODE(t)) {
169 tex = tg;
170 tg = GPARENT(tg);
171 } else
172 tex = t;
173
174 hlevel = LEVEL(hg);
175 tlevel = LEVEL(tg);
176 if (hlevel > tlevel) {
177 raiseLevel(&list, hlevel, hex, tlevel, &hg, pm);
178 hex = hg;
179 hg = GPARENT(hg);
180 } else if (tlevel > hlevel) {
181 raiseLevel(&list, tlevel, tex, hlevel, &tg, pm);
182 tex = tg;
183 tg = GPARENT(tg);
184 }
185
186 /* hg and tg always have the same level */
187 while (hg != tg) {
188 addGraphObjs(&list, hg, NULL, hex, pm);
189 addGraphObjs(&list, tg, tex, NULL, pm);
190 hex = hg;
191 hg = GPARENT(hg);
192 tex = tg;
193 tg = GPARENT(tg);
194 }
195 addGraphObjs(&list, tg, tex, hex, pm);
196
197 return list;
198}
199
200/* compoundEdges:
201 * Construct edges as splines, avoiding clusters when required.
202 * We still don't implement spline multiedges, so we just copy
203 * one spline to all the other edges.
204 * Returns 0 on success. Failure indicates the obstacle configuration
205 * for some edge had overlaps.
206 */
207int compoundEdges(graph_t * g, expand_t* pm, int edgetype)
208{
209 (void)edgetype;
210
211 node_t *n;
212 node_t *head;
213 edge_t *e;
214 edge_t *e0;
215 vconfig_t *vconfig = NULL;
216 int rv = 0;
217
218 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
219 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
220 head = aghead(e);
221 if (n == head && ED_count(e)) { /* self arc */
223 } else if (ED_count(e)) {
224 objlist_t objl = objectList(e, pm);
225 assert(objlist_size(&objl) <= INT_MAX);
226 if (Plegal_arrangement(objlist_at(&objl, 0), (int)objlist_size(&objl))) {
227 vconfig = Pobsopen(objlist_at(&objl, 0), (int)objlist_size(&objl));
228 if (!vconfig) {
229 agwarningf("compoundEdges: could not construct obstacles - falling back to straight line edges\n");
230 rv = 1;
231 objlist_free(&objl);
232 continue;
233 }
234 }
235 else {
236 if (rv == 0) {
237 expand_t margin = sepFactor(g);
238 int pack = getPack (g, CL_OFFSET, CL_OFFSET);
239 agwarningf("compoundEdges: nodes touch - falling back to straight line edges\n");
240 if (pack <= pm->x || pack <= pm->y)
241 agerr(AGPREV, "pack value %d is smaller than esep (%.03f,%.03f)\n", pack, pm->x, pm->y);
242 else if (margin.x <= pm->x || margin.y <= pm->y)
243 agerr(AGPREV, "sep value (%.03f,%.03f) is smaller than esep (%.03f,%.03f)\n",
244 margin.x, margin.y, pm->x, pm->y);
245 rv = 1;
246 }
247 objlist_free(&objl);
248 continue;
249 }
250
251 /* For efficiency, it should be possible to copy the spline
252 * from the first edge to the rest. However, one has to deal
253 * with change in direction, different arrowheads, labels, etc.
254 */
255 for (e0 = e; e0; e0 = ED_to_virt(e0)) {
256 ED_path(e0) = getPath(e0, vconfig, false);
257 assert(objlist_size(&objl) <= INT_MAX);
258 makeSpline(e0, objlist_at(&objl, 0), (int)objlist_size(&objl), false);
259 }
260 objlist_free(&objl);
261 }
262 }
263 }
264 if (vconfig != NULL) {
265 Pobsclose(vconfig);
266 }
267 return rv;
268}
expand_t sepFactor(graph_t *g)
Definition adjust.c:1074
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define PARENT(n)
Definition circular.h:83
static objlist_t objectList(edge_t *ep, expand_t *pm)
static void raiseLevel(objlist_t *l, int maxlvl, void *ex, int minlvl, graph_t **gp, expand_t *pm)
int compoundEdges(graph_t *g, expand_t *pm, int edgetype)
static void addGraphObjs(objlist_t *l, graph_t *g, void *tex, void *hex, expand_t *pm)
static Ppoly_t * makeClustObs(graph_t *g, expand_t *pm)
#define CL_OFFSET
Definition const.h:151
vconfig_t * Pobsopen(Ppoly_t **obs, int n_obs)
Definition cvt.c:26
void Pobsclose(vconfig_t *config)
Definition cvt.c:87
#define head
Definition dthdr.h:15
double deltax
Definition geometry.c:16
double deltay
Definition geometry.c:17
node NULL
Definition grammar.y:149
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define ED_count(e)
Definition types.h:580
#define agtail(e)
Definition cgraph.h:889
#define ED_path(e)
Definition types.h:593
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
#define ED_to_virt(e)
Definition types.h:599
void agwarningf(const char *fmt,...)
Definition agerror.c:173
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:155
@ AGPREV
Definition cgraph.h:858
#define GD_clust(g)
Definition types.h:360
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_nodesep(g)
Definition types.h:394
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
int Plegal_arrangement(Ppoly_t **polys, int n_polys)
Definition legal.c:413
#define DEFINE_LIST(name, type)
Definition list.h:26
#define IS_CLUST_NODE(n)
Definition macros.h:23
NEATOPROCS_API void makeSelfArcs(edge_t *e, int stepx)
NEATOPROCS_API void makeSpline(edge_t *, Ppoly_t **, int, bool)
NEATOPROCS_API Ppolyline_t getPath(edge_t *, vconfig_t *, bool)
NEATOPROCS_API Ppoly_t * makeObstacle(node_t *n, expand_t *, bool)
int getPack(Agraph_t *g, int not_def, int dflt)
Definition pack.c:1236
support for connected components
graph or subgraph
Definition cgraph.h:425
size_t pn
Definition pathgeom.h:47
Ppoint_t * ps
Definition pathgeom.h:46
double x
Definition pathgeom.h:38
double y
Definition pathgeom.h:38
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
double x
Definition adjust.h:39
double y
Definition adjust.h:39
bool doAdd
Definition adjust.h:40
double x
Definition geom.h:29
double y
Definition geom.h:29