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