Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
circle.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#include <assert.h>
12#include <cgraph/alloc.h>
13#include <cgraph/gv_ctype.h>
14#include <cgraph/gv_math.h>
15#include <cgraph/queue.h>
16#include <cgraph/streq.h>
17#include <twopigen/circle.h>
18#include <inttypes.h>
19#include <limits.h>
20#include <math.h>
21#include <stdbool.h>
22#include <stdint.h>
23#include <stdlib.h>
24#include <string.h>
25#define DEF_RANKSEP 1.00
26#define UNSET 10.00
27
28/* dfs to set distance from a particular leaf.
29 * Note that termination is implicit in the test
30 * for reduced number of steps. Proof?
31 */
33{
34 Agnode_t *next;
35
36 uint64_t nsteps = SLEAF(n) + 1;
37
38 for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
39 if ((next = agtail(ep)) == n)
40 next = aghead(ep);
41
42 if (prev == next)
43 continue;
44
45 if (nsteps < SLEAF(next)) { /* handles loops and multiedges */
46 SLEAF(next) = nsteps;
47 setNStepsToLeaf(g, next, n);
48 }
49 }
50}
51
52/* isLeaf:
53 * Return true if n is a leaf node.
54 */
55static bool isLeaf(Agraph_t * g, Agnode_t * n)
56{
57 Agnode_t *neighp = 0;
58 Agnode_t *np;
59
60 for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
61 if ((np = agtail(ep)) == n)
62 np = aghead(ep);
63 if (n == np)
64 continue; /* loop */
65 if (neighp) {
66 if (neighp != np)
67 return false; /* two different neighbors */
68 } else
69 neighp = np;
70 }
71 return true;
72}
73
74static void initLayout(Agraph_t * g)
75{
76 int nnodes = agnnodes(g);
77 assert(nnodes >= 0);
78 uint64_t INF = (uint64_t)nnodes * (uint64_t)nnodes;
79
80 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
81 SCENTER(n) = INF;
82 THETA(n) = UNSET; /* marks theta as unset, since 0 <= theta <= 2PI */
83 if (isLeaf(g, n))
84 SLEAF(n) = 0;
85 else
86 SLEAF(n) = INF;
87 }
88}
89
90/*
91 * Working recursively in from each leaf node (ie, each node
92 * with nStepsToLeaf == 0; see initLayout), set the
93 * minimum value of nStepsToLeaf for each node. Using
94 * that information, assign some node to be the centerNode.
95*/
97{
99 uint64_t maxNStepsToLeaf = 0;
100
101 /* dfs from each leaf node */
102 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
103 if (SLEAF(n) == 0)
104 setNStepsToLeaf(g, n, 0);
105 }
106
107 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
108 if (center == NULL || SLEAF(n) > maxNStepsToLeaf) {
109 maxNStepsToLeaf = SLEAF(n);
110 center = n;
111 }
112 }
113 return center;
114}
115
116/* bfs to create tree structure */
118{
119 Agnode_t *next;
120 Agsym_t* wt = agfindedgeattr(g,"weight");
121 queue_t q = {0};
122
123 queue_push(&q,n);
124 while ((n = queue_pop(&q))) {
125 uint64_t nsteps = SCENTER(n) + 1;
126 for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
127 if (wt && streq(ag_xget(ep,wt),"0")) continue;
128 if ((next = agtail(ep)) == n)
129 next = aghead(ep);
130 if (nsteps < SCENTER(next)) {
131 SCENTER(next) = nsteps;
132 SPARENT(next) = n;
133 NCHILD(n)++;
134 queue_push(&q, next);
135 }
136 }
137 }
138 queue_free(&q);
139}
140
141/*
142 * Work out from the center and determine the value of
143 * nStepsToCenter and parent node for each node.
144 * Return UINT64_MAX if some node was not reached.
145 */
146static uint64_t setParentNodes(Agraph_t * sg, Agnode_t * center)
147{
148 uint64_t maxn = 0;
149 uint64_t unset = SCENTER(center);
150
151 SCENTER(center) = 0;
152 SPARENT(center) = 0;
154
155 /* find the maximum number of steps from the center */
156 for (Agnode_t *n = agfstnode(sg); n; n = agnxtnode(sg, n)) {
157 if (SCENTER(n) == unset) {
158 return UINT64_MAX;
159 }
160 else if (SCENTER(n) > maxn) {
161 maxn = SCENTER(n);
162 }
163 }
164 return maxn;
165}
166
167/* Sets each node's subtreeSize, which counts the number of
168 * leaves in subtree rooted at the node.
169 * At present, this is done bottom-up.
170 */
171static void setSubtreeSize(Agraph_t * g)
172{
173 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
174 if (NCHILD(n) > 0)
175 continue;
176 STSIZE(n)++;
178 STSIZE(parent)++;
179 }
180 }
181}
182
184{
185 Agnode_t *next;
186
187 double ratio = SPAN(n) / STSIZE(n);
188 for (Agedge_t *ep = agfstedge(g, n); ep; ep = agnxtedge(g, ep, n)) {
189 if ((next = agtail(ep)) == n)
190 next = aghead(ep);
191 if (SPARENT(next) != n)
192 continue; /* handles loops */
193
194 if (SPAN(next) != 0.0)
195 continue; /* multiedges */
196 SPAN(next) = ratio * STSIZE(next);
197
198 if (NCHILD(next) > 0) {
199 setChildSubtreeSpans(g, next);
200 }
201 }
202}
203
205{
206 SPAN(center) = 2 * M_PI;
208}
209
211static bool is_set(double a) { return !is_exactly_equal(a, UNSET); }
212
213 /* Set the node positions for the 2nd and later rings. */
214static void setChildPositions(Agraph_t * sg, Agnode_t * n)
215{
216 Agnode_t *next;
217 double theta; /* theta is the lower boundary radius of the fan */
218
219 if (SPARENT(n) == 0) /* center */
220 theta = 0;
221 else
222 theta = THETA(n) - SPAN(n) / 2;
223
224 for (Agedge_t *ep = agfstedge(sg, n); ep; ep = agnxtedge(sg, ep, n)) {
225 if ((next = agtail(ep)) == n)
226 next = aghead(ep);
227 if (SPARENT(next) != n)
228 continue; /* handles loops */
229 if (is_set(THETA(next)))
230 continue; /* handles multiedges */
231
232 THETA(next) = theta + SPAN(next) / 2.0;
233 theta += SPAN(next);
234
235 if (NCHILD(next) > 0)
236 setChildPositions(sg, next);
237 }
238}
239
241{
242 THETA(center) = 0;
244}
245
246/* getRankseps:
247 * Return array of doubles of size maxrank+1 containing the radius of each
248 * rank. Position 0 always contains 0. Use the colon-separated list of
249 * doubles provided by ranksep to get the deltas for each additional rank.
250 * If not enough values are provided, the last value is repeated.
251 * If the ranksep attribute is not provided, use DEF_RANKSEP for all values.
252 */
253static double*
254getRankseps (Agraph_t* g, uint64_t maxrank)
255{
256 char *p;
257 char *endp;
258 char c;
259 uint64_t rk = 1;
260 double* ranks = gv_calloc(maxrank + 1, sizeof(double));
261 double xf = 0.0, delx = 0.0, d;
262
263 if ((p = late_string(g, agfindgraphattr(g->root, "ranksep"), NULL))) {
264 while (rk <= maxrank && (d = strtod (p, &endp)) > 0) {
265 delx = fmax(d, MIN_RANKSEP);
266 xf += delx;
267 ranks[rk++] = xf;
268 p = endp;
269 while ((c = *p) && (gv_isspace(c) || c == ':'))
270 p++;
271 }
272 }
273 else {
274 delx = DEF_RANKSEP;
275 }
276
277 for (uint64_t i = rk; i <= maxrank; i++) {
278 xf += delx;
279 ranks[i] = xf;
280 }
281
282 return ranks;
283}
284
285static void setAbsolutePos(Agraph_t * g, uint64_t maxrank)
286{
287 double* ranksep = getRankseps (g, maxrank);
288 if (Verbose) {
289 fputs ("Rank separation = ", stderr);
290 for (uint64_t i = 0; i <= maxrank; i++)
291 fprintf (stderr, "%.03lf ", ranksep[i]);
292 fputs ("\n", stderr);
293 }
294
295 /* Convert circular to cartesian coordinates */
296 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
297 double hyp = ranksep[SCENTER(n)];
298 ND_pos(n)[0] = hyp * cos(THETA(n));
299 ND_pos(n)[1] = hyp * sin(THETA(n));
300 }
301 free (ranksep);
302}
303
304/* circleLayout:
305 * We assume sg is is connected and non-empty.
306 * Also, if center != 0, we are guaranteed that center is
307 * in the graph.
308 */
310{
311 if (agnnodes(sg) == 1) {
312 Agnode_t *n = agfstnode(sg);
313 ND_pos(n)[0] = 0;
314 ND_pos(n)[1] = 0;
315 return center;
316 }
317
318 initLayout(sg);
319
320 if (!center)
322
323 uint64_t maxNStepsToCenter = setParentNodes(sg,center);
324 if (Verbose)
325 fprintf(stderr, "root = %s max steps to root = %" PRIu64 "\n",
326 agnameof(center), maxNStepsToCenter);
327 if (maxNStepsToCenter == UINT64_MAX) {
328 agerrorf("twopi: use of weight=0 creates disconnected component.\n");
329 return center;
330 }
331
332 setSubtreeSize(sg);
333
335
336 setPositions(sg, center);
337
338 setAbsolutePos(sg, maxNStepsToCenter);
339 return center;
340}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define M_PI
Definition arith.h:41
static void setNStepsToCenter(Agraph_t *g, Agnode_t *n)
Definition circle.c:117
static double * getRankseps(Agraph_t *g, uint64_t maxrank)
Definition circle.c:254
static void setPositions(Agraph_t *sg, Agnode_t *center)
Definition circle.c:240
static void setSubtreeSize(Agraph_t *g)
Definition circle.c:171
static void setChildPositions(Agraph_t *sg, Agnode_t *n)
Definition circle.c:214
static void setChildSubtreeSpans(Agraph_t *g, Agnode_t *n)
Definition circle.c:183
static bool is_set(double a)
has the given value been assigned?
Definition circle.c:211
static void setNStepsToLeaf(Agraph_t *g, Agnode_t *n, Agnode_t *prev)
Definition circle.c:32
static void setAbsolutePos(Agraph_t *g, uint64_t maxrank)
Definition circle.c:285
static void initLayout(Agraph_t *g)
Definition circle.c:74
static Agnode_t * findCenterNode(Agraph_t *g)
Definition circle.c:96
Agnode_t * circleLayout(Agraph_t *sg, Agnode_t *center)
Definition circle.c:309
#define UNSET
Definition circle.c:26
static void setSubtreeSpans(Agraph_t *sg, Agnode_t *center)
Definition circle.c:204
#define DEF_RANKSEP
Definition circle.c:25
static uint64_t setParentNodes(Agraph_t *sg, Agnode_t *center)
Definition circle.c:146
static bool isLeaf(Agraph_t *g, Agnode_t *n)
Definition circle.c:55
#define SLEAF(n)
Definition circle.h:30
#define THETA(n)
Definition circle.h:36
#define NCHILD(n)
Definition circle.h:32
#define SCENTER(n)
Definition circle.h:33
#define SPAN(n)
Definition circle.h:35
#define STSIZE(n)
Definition circle.h:31
#define parent(i)
Definition closest.c:78
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:78
#define MIN_RANKSEP
Definition const.h:88
static int Verbose
Definition gml2gv.c:22
void free(void *)
node NULL
Definition grammar.y:149
int agnnodes(Agraph_t *g)
Definition graph.c:158
#define agfindedgeattr(g, a)
Definition types.h:617
#define agtail(e)
Definition cgraph.h:889
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:93
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:84
void agerrorf(const char *fmt,...)
Definition agerror.c:165
#define agfindgraphattr(g, a)
Definition types.h:613
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_pos(n)
Definition types.h:520
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
replacements for ctype.h functions
static bool gv_isspace(int c)
Definition gv_ctype.h:55
Arithmetic helper functions.
static bool is_exactly_equal(double a, double b)
are two values precisely the same?
Definition gv_math.h:43
$2 u p prev
Definition htmlparse.y:495
#define SPARENT(n)
Definition patchwork.h:25
generic first-in-first-out buffer (queue)
static void queue_free(queue_t *q)
Definition queue.h:108
static void queue_push(queue_t *q, void *item)
Definition queue.h:52
static void * queue_pop(queue_t *q)
Definition queue.h:90
#define INF
Definition sccmap.c:41
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
graph or subgraph
Definition cgraph.h:425
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:434
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:639
static point center(point vertex[], size_t n)
#define ag_xget(x, a)
Definition types.h:606