Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
utils.c
Go to the documentation of this file.
1
3/*************************************************************************
4 * Copyright (c) 2011 AT&T Intellectual Property
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * https://www.eclipse.org/legal/epl-v10.html
9 *
10 * Contributors: Details at https://graphviz.org
11 *************************************************************************/
12
13#include <common/render.h>
14#include <cgraph/alloc.h>
15#include <cgraph/agxbuf.h>
16#include <cgraph/gv_ctype.h>
17#include <cgraph/gv_math.h>
18#include <cgraph/strview.h>
19#include <cgraph/tokenize.h>
20#include <common/htmltable.h>
21#include <common/entities.h>
22#include <limits.h>
23#include <math.h>
24#include <gvc/gvc.h>
25#include <cgraph/startswith.h>
26#include <cgraph/strcasecmp.h>
27#include <cgraph/streq.h>
28#include <stddef.h>
29#include <stdbool.h>
30#include <stdint.h>
31#include <unistd.h>
32
33int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum) {
34 if (attr == NULL)
35 return defaultValue;
36 char *p = ag_xget(obj, attr);
37 if (!p || p[0] == '\0')
38 return defaultValue;
39 char *endp;
40 long rv = strtol(p, &endp, 10);
41 if (p == endp || rv > INT_MAX)
42 return defaultValue; /* invalid int format */
43 if (rv < minimum)
44 return minimum;
45 return (int)rv;
46}
47
48double late_double(void *obj, attrsym_t *attr, double defaultValue,
49 double minimum) {
50 if (!attr || !obj)
51 return defaultValue;
52 char *p = ag_xget(obj, attr);
53 if (!p || p[0] == '\0')
54 return defaultValue;
55 char *endp;
56 double rv = strtod(p, &endp);
57 if (p == endp)
58 return defaultValue; /* invalid double format */
59 if (rv < minimum)
60 return minimum;
61 return rv;
62}
63
72 if (PSinputscale > 0) return PSinputscale; /* command line flag prevails */
73 double d = late_double(g, agfindgraphattr(g, "inputscale"), -1, 0);
74 if (is_exactly_zero(d)) return POINTS_PER_INCH;
75 return d;
76}
77
78char *late_string(void *obj, attrsym_t *attr, char *defaultValue) {
79 if (!attr || !obj)
80 return defaultValue;
81 return agxget(obj, attr);
82}
83
84char *late_nnstring(void *obj, attrsym_t *attr, char *defaultValue) {
85 char *rv = late_string(obj, attr, defaultValue);
86 if (!rv || (rv[0] == '\0'))
87 return defaultValue;
88 return rv;
89}
90
91bool late_bool(void *obj, attrsym_t *attr, bool defaultValue) {
92 if (attr == NULL)
93 return defaultValue;
94
95 return mapbool(agxget(obj, attr));
96}
97
98/* union-find */
100{
101 while (ND_UF_parent(n) && ND_UF_parent(n) != n) {
104 n = ND_UF_parent(n);
105 }
106 return n;
107}
108
110{
111 if (u == v)
112 return u;
113 if (ND_UF_parent(u) == NULL) {
114 ND_UF_parent(u) = u;
115 ND_UF_size(u) = 1;
116 } else
117 u = UF_find(u);
118 if (ND_UF_parent(v) == NULL) {
119 ND_UF_parent(v) = v;
120 ND_UF_size(v) = 1;
121 } else
122 v = UF_find(v);
123 /* if we have two copies of the same node, their union is just that node */
124 if (u == v)
125 return u;
126 if (ND_id(u) > ND_id(v)) {
127 ND_UF_parent(u) = v;
128 ND_UF_size(v) += ND_UF_size(u);
129 } else {
130 ND_UF_parent(v) = u;
131 ND_UF_size(u) += ND_UF_size(v);
132 v = u;
133 }
134 return v;
135}
136
138{
139 ND_UF_size(u) = 1;
140 ND_UF_parent(u) = NULL;
141 ND_ranktype(u) = NORMAL;
142}
143
145{
146 assert(u == UF_find(u));
147 ND_UF_parent(u) = v;
148 ND_UF_size(v) += ND_UF_size(u);
149}
150
152{
153 pointf r;
154
155 r.x = POINTS_PER_INCH * ND_pos(n)[0];
156 r.y = POINTS_PER_INCH * ND_pos(n)[1];
157 return r;
158}
159
160/* from Glassner's Graphics Gems */
161#define W_DEGREE 5
162
163/*
164 * Evaluate a Bezier curve at a particular parameter value
165 * Fill in control points for resulting sub-curves if "Left" and
166 * "Right" are non-null.
167 *
168 */
169pointf Bezier(pointf *V, double t, pointf *Left, pointf *Right) {
170 const int degree = 3;
171 int i, j; /* Index variables */
172 pointf Vtemp[W_DEGREE + 1][W_DEGREE + 1];
173
174 /* Copy control points */
175 for (j = 0; j <= degree; j++) {
176 Vtemp[0][j] = V[j];
177 }
178
179 /* Triangle computation */
180 for (i = 1; i <= degree; i++) {
181 for (j = 0; j <= degree - i; j++) {
182 Vtemp[i][j].x =
183 (1.0 - t) * Vtemp[i - 1][j].x + t * Vtemp[i - 1][j + 1].x;
184 Vtemp[i][j].y =
185 (1.0 - t) * Vtemp[i - 1][j].y + t * Vtemp[i - 1][j + 1].y;
186 }
187 }
188
189 if (Left != NULL)
190 for (j = 0; j <= degree; j++)
191 Left[j] = Vtemp[j][0];
192 if (Right != NULL)
193 for (j = 0; j <= degree; j++)
194 Right[j] = Vtemp[degree - j][j];
195
196 return Vtemp[degree][0];
197}
198
199#ifdef DEBUG
200edge_t *debug_getedge(graph_t * g, char *s0, char *s1)
201{
202 node_t *n0, *n1;
203 n0 = agfindnode(g, s0);
204 n1 = agfindnode(g, s1);
205 if (n0 && n1)
206 return agfindedge(g, n0, n1);
207 return NULL;
208}
209Agraphinfo_t* GD_info(graph_t * g) { return ((Agraphinfo_t*)AGDATA(g));}
210Agnodeinfo_t* ND_info(node_t * n) { return ((Agnodeinfo_t*)AGDATA(n));}
211#endif
212
213/* safefile:
214 * Check to make sure it is okay to read in files.
215 * It returns NULL if the filename is trivial.
216 *
217 * If the application has set the SERVER_NAME environment variable,
218 * this indicates it is web-active.
219 *
220 * If filename contains multiple components, the user is
221 * warned, once, that everything to the left is ignored.
222 *
223 * For non-server applications, we use the path list in Gvimagepath to
224 * resolve relative pathnames.
225 *
226 * N.B. safefile uses a fixed buffer, so functions using it should use the
227 * value immediately or make a copy.
228 */
229#ifdef _WIN32
230#define PATHSEP ";"
231#else
232#define PATHSEP ":"
233#endif
234
235static strview_t *mkDirlist(const char *list) {
236 size_t cnt = 0;
237 strview_t *dirs = gv_calloc(1, sizeof(strview_t));
238
239 for (tok_t t = tok(list, PATHSEP); !tok_end(&t); tok_next(&t)) {
240 strview_t dir = tok_get(&t);
241 dirs = gv_recalloc(dirs, cnt + 1, cnt + 2, sizeof(strview_t));
242 dirs[cnt++] = dir;
243 }
244 return dirs;
245}
246
247static char *findPath(const strview_t *dirs, const char *str) {
248 static agxbuf safefilename;
249
250 for (const strview_t *dp = dirs; dp != NULL && dp->data != NULL; dp++) {
251 agxbprint(&safefilename, "%.*s%s%s", (int)dp->size, dp->data, DIRSEP, str);
252 char *filename = agxbuse(&safefilename);
253 if (access(filename, R_OK) == 0)
254 return filename;
255 }
256 return NULL;
257}
258
259const char *safefile(const char *filename)
260{
261 static bool onetime = true;
262 static char *pathlist = NULL;
263 static strview_t *dirs;
264
265 if (!filename || !filename[0])
266 return NULL;
267
268 if (HTTPServerEnVar) { /* If used as a server */
269 if (onetime) {
271 "file loading is disabled because the environment contains SERVER_NAME=\"%s\"\n",
273 onetime = false;
274 }
275 return NULL;
276 }
277
278 if (Gvfilepath != NULL) {
279 if (pathlist == NULL) {
280 free(dirs);
281 pathlist = Gvfilepath;
282 dirs = mkDirlist(pathlist);
283 }
284
285 const char *str = filename;
286 for (const char *sep = "/\\:"; *sep != '\0'; ++sep) {
287 const char *p = strrchr(str, *sep);
288 if (p != NULL) {
289 str = ++p;
290 }
291 }
292
293 return findPath(dirs, str);
294 }
295
296 if (pathlist != Gvimagepath) {
297 free (dirs);
298 dirs = NULL;
299 pathlist = Gvimagepath;
300 if (pathlist && *pathlist)
301 dirs = mkDirlist(pathlist);
302 }
303
304 if (*filename == DIRSEP[0] || !dirs)
305 return filename;
306
307 return findPath(dirs, filename);
308}
309
310int maptoken(char *p, char **name, int *val) {
311 char *q;
312
313 int i = 0;
314 for (; (q = name[i]) != 0; i++)
315 if (p && streq(p, q))
316 break;
317 return val[i];
318}
319
320bool mapBool(const char *p, bool defaultValue) {
321 if (!p || *p == '\0')
322 return defaultValue;
323 if (!strcasecmp(p, "false"))
324 return false;
325 if (!strcasecmp(p, "no"))
326 return false;
327 if (!strcasecmp(p, "true"))
328 return true;
329 if (!strcasecmp(p, "yes"))
330 return true;
331 if (gv_isdigit(*p))
332 return atoi(p) != 0;
333 return defaultValue;
334}
335
336bool mapbool(const char *p)
337{
338 return mapBool(p, false);
339}
340
342{
343 double bestdist2, d2, dlow2, dhigh2; /* squares of distances */
344 double low, high, t;
345 pointf c[4], pt2;
346 bezier bz;
347
348 size_t besti = SIZE_MAX;
349 size_t bestj = SIZE_MAX;
350 bestdist2 = 1e+38;
351 for (size_t i = 0; i < spl->size; i++) {
352 bz = spl->list[i];
353 for (size_t j = 0; j < bz.size; j++) {
354 pointf b;
355
356 b.x = bz.list[j].x;
357 b.y = bz.list[j].y;
358 d2 = DIST2(b, pt);
359 if (bestj == SIZE_MAX || d2 < bestdist2) {
360 besti = i;
361 bestj = j;
362 bestdist2 = d2;
363 }
364 }
365 }
366
367 bz = spl->list[besti];
368 /* Pick best Bezier. If bestj is the last point in the B-spline, decrement.
369 * Then set j to be the first point in the corresponding Bezier by dividing
370 * then multiplying be 3. Thus, 0,1,2 => 0; 3,4,5 => 3, etc.
371 */
372 if (bestj == bz.size-1)
373 bestj--;
374 const size_t j = 3 * (bestj / 3);
375 for (size_t k = 0; k < 4; k++) {
376 c[k].x = bz.list[j + k].x;
377 c[k].y = bz.list[j + k].y;
378 }
379 low = 0.0;
380 high = 1.0;
381 dlow2 = DIST2(c[0], pt);
382 dhigh2 = DIST2(c[3], pt);
383 do {
384 t = (low + high) / 2.0;
385 pt2 = Bezier(c, t, NULL, NULL);
386 if (fabs(dlow2 - dhigh2) < 1.0)
387 break;
388 if (fabs(high - low) < .00001)
389 break;
390 if (dlow2 < dhigh2) {
391 high = t;
392 dhigh2 = DIST2(pt2, pt);
393 } else {
394 low = t;
395 dlow2 = DIST2(pt2, pt);
396 }
397 } while (1);
398 return pt2;
399}
400
401static int Tflag;
402void gvToggle(int s)
403{
404 (void)s;
405 Tflag = !Tflag;
406#if !defined(_WIN32)
407 signal(SIGUSR1, gvToggle);
408#endif
409}
410
411int test_toggle(void)
412{
413 return Tflag;
414}
415
416struct fontinfo {
417 double fontsize;
418 char *fontname;
420};
421
423{
424 struct fontinfo fi;
425 char *str;
426 ND_width(n) =
428 ND_height(n) =
430 ND_shape(n) =
432 str = agxget(n, N_label);
436 ND_label(n) = make_label(n, str,
438 fi.fontsize, fi.fontname, fi.fontcolor);
439 if (N_xlabel && (str = agxget(n, N_xlabel)) && str[0]) {
441 fi.fontsize, fi.fontname, fi.fontcolor);
443 }
444
445 {
446 const int showboxes = imin(late_int(n, N_showboxes, 0, 0), UCHAR_MAX);
447 ND_showboxes(n) = (unsigned char)showboxes;
448 }
449 ND_shape(n)->fns->initfn(n);
450}
451
458
459static void
461 struct fontinfo *lfi)
462{
463 if (!fi->fontname) initFontEdgeAttr(e, fi);
467}
468
470static bool
472{
473 char *str;
474 bool rv = false;
475
476 if (sym) { /* mapbool isn't a good fit, because we want "" to mean true */
477 str = agxget(e,sym);
478 if (str && str[0]) rv = !mapbool(str);
479 else rv = false;
480 }
481 return rv;
482}
483
484static port
485chkPort (port (*pf)(node_t*, char*, char*), node_t* n, char* s)
486{
487 port pt;
488 char* cp=NULL;
489 if(s)
490 cp= strchr(s,':');
491 if (cp) {
492 *cp = '\0';
493 pt = pf(n, s, cp+1);
494 *cp = ':';
495 pt.name = cp+1;
496 }
497 else {
498 pt = pf(n, s, NULL);
499 pt.name = s;
500 }
501 return pt;
502}
503
504/* return true if edge has label */
506 char *str;
507 struct fontinfo fi;
508 struct fontinfo lfi;
509 graph_t *sg = agraphof(agtail(e));
510
511 fi.fontname = NULL;
512 lfi.fontname = NULL;
513 if (E_label && (str = agxget(e, E_label)) && str[0]) {
514 initFontEdgeAttr(e, &fi);
516 fi.fontsize, fi.fontname, fi.fontcolor);
519 }
520
521 if (E_xlabel && (str = agxget(e, E_xlabel)) && str[0]) {
522 if (!fi.fontname)
523 initFontEdgeAttr(e, &fi);
525 fi.fontsize, fi.fontname, fi.fontcolor);
527 }
528
529 if (E_headlabel && (str = agxget(e, E_headlabel)) && str[0]) {
530 initFontLabelEdgeAttr(e, &fi, &lfi);
532 lfi.fontsize, lfi.fontname, lfi.fontcolor);
534 }
535 if (E_taillabel && (str = agxget(e, E_taillabel)) && str[0]) {
536 if (!lfi.fontname)
537 initFontLabelEdgeAttr(e, &fi, &lfi);
539 lfi.fontsize, lfi.fontname, lfi.fontcolor);
541 }
542
543 /* We still accept ports beginning with colons but this is deprecated
544 * That is, we allow tailport = ":abc" as well as the preferred
545 * tailport = "abc".
546 */
547 str = agget(e, TAIL_ID);
548 /* libgraph always defines tailport/headport; libcgraph doesn't */
549 if (!str) str = "";
550 if (str && str[0])
551 ND_has_port(agtail(e)) = true;
552 ED_tail_port(e) = chkPort (ND_shape(agtail(e))->fns->portfn, agtail(e), str);
553 if (noClip(e, E_tailclip))
554 ED_tail_port(e).clip = false;
555 str = agget(e, HEAD_ID);
556 /* libgraph always defines tailport/headport; libcgraph doesn't */
557 if (!str) str = "";
558 if (str && str[0])
559 ND_has_port(aghead(e)) = true;
560 ED_head_port(e) = chkPort(ND_shape(aghead(e))->fns->portfn, aghead(e), str);
561 if (noClip(e, E_headclip))
562 ED_head_port(e).clip = false;
563}
564
565static boxf addLabelBB(boxf bb, textlabel_t * lp, bool flipxy)
566{
567 double width, height;
568 pointf p = lp->pos;
569 double min, max;
570
571 if (flipxy) {
572 height = lp->dimen.x;
573 width = lp->dimen.y;
574 }
575 else {
576 width = lp->dimen.x;
577 height = lp->dimen.y;
578 }
579 min = p.x - width / 2.;
580 max = p.x + width / 2.;
581 if (min < bb.LL.x)
582 bb.LL.x = min;
583 if (max > bb.UR.x)
584 bb.UR.x = max;
585
586 min = p.y - height / 2.;
587 max = p.y + height / 2.;
588 if (min < bb.LL.y)
589 bb.LL.y = min;
590 if (max > bb.UR.y)
591 bb.UR.y = max;
592
593 return bb;
594}
595
599boxf
601{
602 const size_t sides = poly->sides;
603 const size_t peris = MAX(poly->peripheries, 1ul);
604 pointf* verts = poly->vertices + (peris-1)*sides;
605 boxf bb;
606
607 bb.LL = bb.UR = verts[0];
608 for (size_t i = 1; i < sides; i++) {
609 bb.LL.x = MIN(bb.LL.x,verts[i].x);
610 bb.LL.y = MIN(bb.LL.y,verts[i].y);
611 bb.UR.x = MAX(bb.UR.x,verts[i].x);
612 bb.UR.y = MAX(bb.UR.y,verts[i].y);
613 }
614 return bb;
615}
616
621{
622 GD_bb(g) = addLabelBB(GD_bb(g), lp, GD_flip(g));
623}
624
630{
631 node_t *n;
632 edge_t *e;
633 boxf b, bb;
634 boxf BF;
635 pointf ptf, s2;
636
637 if (agnnodes(g) == 0 && GD_n_cluster(g) == 0) {
638 bb.LL = (pointf){0};
639 bb.UR = (pointf){0};
640 return;
641 }
642
643 bb.LL = (pointf){INT_MAX, INT_MAX};
644 bb.UR = (pointf){-INT_MAX, -INT_MAX};
645 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
646 ptf = coord(n);
647 s2.x = ND_xsize(n) / 2.0;
648 s2.y = ND_ysize(n) / 2.0;
649 b.LL = sub_pointf(ptf, s2);
650 b.UR = add_pointf(ptf, s2);
651
652 EXPANDBB(bb,b);
653 if (ND_xlabel(n) && ND_xlabel(n)->set) {
654 bb = addLabelBB(bb, ND_xlabel(n), GD_flip(g));
655 }
656 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
657 if (ED_spl(e) == 0)
658 continue;
659 for (size_t i = 0; i < ED_spl(e)->size; i++) {
660 for (size_t j = 0; j < (((Agedgeinfo_t*)AGDATA(e))->spl)->list[i].size; j++) {
661 ptf = ED_spl(e)->list[i].list[j];
662 EXPANDBP(bb,ptf);
663 }
664 }
665 if (ED_label(e) && ED_label(e)->set) {
666 bb = addLabelBB(bb, ED_label(e), GD_flip(g));
667 }
668 if (ED_head_label(e) && ED_head_label(e)->set) {
669 bb = addLabelBB(bb, ED_head_label(e), GD_flip(g));
670 }
671 if (ED_tail_label(e) && ED_tail_label(e)->set) {
672 bb = addLabelBB(bb, ED_tail_label(e), GD_flip(g));
673 }
674 if (ED_xlabel(e) && ED_xlabel(e)->set) {
675 bb = addLabelBB(bb, ED_xlabel(e), GD_flip(g));
676 }
677 }
678 }
679
680 for (int i = 1; i <= GD_n_cluster(g); i++) {
681 B2BF(GD_bb(GD_clust(g)[i]), BF);
682 EXPANDBB(bb,BF);
683 }
684 if (GD_label(g) && GD_label(g)->set) {
685 bb = addLabelBB(bb, GD_label(g), GD_flip(g));
686 }
687
688 GD_bb(g) = bb;
689}
690
692{
693 return g == g->root || !strncasecmp(agnameof(g), "cluster", 7) ||
694 mapbool(agget(g, "cluster"));
695}
696
700Agsym_t *setAttr(graph_t * g, void *obj, char *name, char *value,
701 Agsym_t * ap)
702{
703 if (ap == NULL) {
704 switch (agobjkind(obj)) {
705 case AGRAPH:
706 ap = agattr(g, AGRAPH,name, "");
707 break;
708 case AGNODE:
709 ap = agattr(g,AGNODE, name, "");
710 break;
711 case AGEDGE:
712 ap = agattr(g,AGEDGE, name, "");
713 break;
714 }
715 }
716 agxset(obj, ap, value);
717 return ap;
718}
719
725static node_t *clustNode(node_t * n, graph_t * cg, agxbuf * xb,
726 graph_t * clg)
727{
728 node_t *cn;
729 static int idx = 0;
730
731 agxbprint(xb, "__%d:%s", idx++, agnameof(cg));
732
733 cn = agnode(agroot(cg), agxbuse(xb), 1);
734 agbindrec(cn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
735
736 SET_CLUST_NODE(cn);
737 agsubnode(cg,cn,1);
738 agsubnode(clg,n,1);
739
740 /* set attributes */
741 N_label = setAttr(agraphof(cn), cn, "label", "", N_label);
742 N_style = setAttr(agraphof(cn), cn, "style", "invis", N_style);
743 N_shape = setAttr(agraphof(cn), cn, "shape", "box", N_shape);
744
745 return cn;
746}
747
748typedef struct {
749 Dtlink_t link; /* cdt data */
750 void *p[2]; /* key */
753} item;
754
755static int cmpItem(void *pp1, void *pp2) {
756 const void **p1 = pp1;
757 const void **p2 = pp2;
758 if (p1[0] < p2[0])
759 return -1;
760 if (p1[0] > p2[0])
761 return 1;
762 if (p1[1] < p2[1])
763 return -1;
764 if (p1[1] > p2[1])
765 return 1;
766 return 0;
767}
768
769static void *newItem(item *objp, Dtdisc_t *disc) {
770 item *newp = gv_alloc(sizeof(item));
771
772 (void)disc;
773 newp->p[0] = objp->p[0];
774 newp->p[1] = objp->p[1];
775 newp->t = objp->t;
776 newp->h = objp->h;
777
778 return newp;
779}
780
782 .key = offsetof(item, p),
783 .size = sizeof(2 * sizeof(void *)),
784 .link = offsetof(item, link),
785 .makef = (Dtmake_f)newItem,
786 .freef = free,
787 .comparf = cmpItem,
788};
789
791static edge_t *cloneEdge(edge_t * e, node_t * ct, node_t * ch)
792{
793 graph_t *g = agraphof(ct);
794 edge_t *ce = agedge(g, ct, ch,NULL,1);
795 agbindrec(ce, "Agedgeinfo_t", sizeof(Agedgeinfo_t), true);
796 agcopyattr(e, ce);
797 ED_compound(ce) = true;
798
799 return ce;
800}
801
802static void insertEdge(Dt_t * map, void *t, void *h, edge_t * e)
803{
804 item dummy;
805
806 dummy.p[0] = t;
807 dummy.p[1] = h;
808 dummy.t = agtail(e);
809 dummy.h = aghead(e);
810 dtinsert(map, &dummy);
811
812 dummy.p[0] = h;
813 dummy.p[1] = t;
814 dummy.t = aghead(e);
815 dummy.h = agtail(e);
816 dtinsert(map, &dummy);
817}
818
820static item *mapEdge(Dt_t * map, edge_t * e)
821{
822 void *key[2];
823
824 key[0] = agtail(e);
825 key[1] = aghead(e);
826 return dtmatch(map, &key);
827}
828
844static graph_t *mapc(Dt_t *cmap, node_t *n) {
845 if (startswith(agnameof(n), "cluster")) {
846 return findCluster(cmap, agnameof(n));
847 }
848 return NULL;
849}
850
851static int
852checkCompound(edge_t * e, graph_t * clg, agxbuf * xb, Dt_t * map, Dt_t* cmap)
853{
854 node_t *cn;
855 node_t *cn1;
856 node_t *t = agtail(e);
857 node_t *h = aghead(e);
858 edge_t *ce;
859 item *ip;
860
861 if (IS_CLUST_NODE(h)) return 0;
862 graph_t *const tg = mapc(cmap, t);
863 graph_t *const hg = mapc(cmap, h);
864 if (!tg && !hg)
865 return 0;
866 if (tg == hg) {
867 agwarningf("cluster cycle %s -- %s not supported\n", agnameof(t),
868 agnameof(t));
869 return 0;
870 }
871 ip = mapEdge(map, e);
872 if (ip) {
873 cloneEdge(e, ip->t, ip->h);
874 return 1;
875 }
876
877 if (hg) {
878 if (tg) {
879 if (agcontains(hg, tg)) {
880 agwarningf("tail cluster %s inside head cluster %s\n",
881 agnameof(tg), agnameof(hg));
882 return 0;
883 }
884 if (agcontains(tg, hg)) {
885 agwarningf("head cluster %s inside tail cluster %s\n",
886 agnameof(hg),agnameof(tg));
887 return 0;
888 }
889 cn = clustNode(t, tg, xb, clg);
890 cn1 = clustNode(h, hg, xb, clg);
891 ce = cloneEdge(e, cn, cn1);
892 insertEdge(map, t, h, ce);
893 } else {
894 if (agcontains(hg, t)) {
895 agwarningf("tail node %s inside head cluster %s\n",
896 agnameof(t), agnameof(hg));
897 return 0;
898 }
899 cn = clustNode(h, hg, xb, clg);
900 ce = cloneEdge(e, t, cn);
901 insertEdge(map, t, h, ce);
902 }
903 } else {
904 if (agcontains(tg, h)) {
905 agwarningf("head node %s inside tail cluster %s\n", agnameof(h),
906 agnameof(tg));
907 return 0;
908 }
909 cn = clustNode(t, tg, xb, clg);
910 ce = cloneEdge(e, cn, h);
911 insertEdge(map, t, h, ce);
912 }
913 return 1;
914}
915
916typedef struct {
919} cl_edge_t;
920
921static int
923{
924 cl_edge_t* cl_info = (cl_edge_t*)HAS_CLUST_EDGE(g);
925 if (cl_info)
926 return cl_info->n_cluster_edges;
927 return 0;
928}
929
937{
938 int num_cl_edges = 0;
939 node_t *n;
940 node_t *nxt;
941 edge_t *e;
942 graph_t *clg;
943 agxbuf xb = {0};
944 Dt_t *map;
945 Dt_t *cmap = mkClustMap (g);
946
947 map = dtopen(&mapDisc, Dtoset);
948 clg = agsubg(g, "__clusternodes",1);
949 agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
950 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
951 if (IS_CLUST_NODE(n)) continue;
952 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
953 num_cl_edges += checkCompound(e, clg, &xb, map, cmap);
954 }
955 }
956 agxbfree(&xb);
957 dtclose(map);
958 for (n = agfstnode(clg); n; n = nxt) {
959 nxt = agnxtnode(clg, n);
960 agdelete(g, n);
961 }
962 agclose(clg);
963 if (num_cl_edges) {
964 cl_edge_t* cl_info;
965 cl_info = agbindrec(g, CL_EDGE_TAG, sizeof(cl_edge_t), false);
966 cl_info->n_cluster_edges = num_cl_edges;
967 }
968 dtclose(cmap);
969}
970
978static node_t *mapN(node_t * n, graph_t * clg)
979{
980 node_t *nn;
981 char *name;
982 graph_t *g = agraphof(n);
983 Agsym_t *sym;
984
985 if (!IS_CLUST_NODE(n))
986 return n;
987 agsubnode(clg, n, 1);
988 name = strchr(agnameof(n), ':');
989 assert(name);
990 name++;
991 if ((nn = agfindnode(g, name)))
992 return nn;
993 nn = agnode(g, name, 1);
994 agbindrec(nn, "Agnodeinfo_t", sizeof(Agnodeinfo_t), true);
995 SET_CLUST_NODE(nn);
996
997 /* Set all attributes to default */
998 for (sym = agnxtattr(g, AGNODE, NULL); sym; (sym = agnxtattr(g, AGNODE, sym))) {
999 if (agxget(nn, sym) != sym->defval)
1000 agxset(nn, sym, sym->defval);
1001 }
1002 return nn;
1003}
1004
1005static void undoCompound(edge_t * e, graph_t * clg)
1006{
1007 node_t *t = agtail(e);
1008 node_t *h = aghead(e);
1009 node_t *ntail;
1010 node_t *nhead;
1011 edge_t* ce;
1012
1013 ntail = mapN(t, clg);
1014 nhead = mapN(h, clg);
1015 ce = cloneEdge(e, ntail, nhead);
1016
1017 /* transfer drawing information */
1018 ED_spl(ce) = ED_spl(e);
1019 ED_spl(e) = NULL;
1020 ED_label(ce) = ED_label(e);
1021 ED_label(e) = NULL;
1022 ED_xlabel(ce) = ED_xlabel(e);
1023 ED_xlabel(e) = NULL;
1025 ED_head_label(e) = NULL;
1027 ED_tail_label(e) = NULL;
1028 gv_cleanup_edge(e);
1029}
1030
1036{
1037 node_t *n;
1038 node_t *nextn;
1039 edge_t *e;
1040 graph_t *clg;
1041 int ecnt = num_clust_edges(g);
1042 int i = 0;
1043
1044 if (!ecnt) return;
1045 clg = agsubg(g, "__clusternodes",1);
1046 agbindrec(clg, "Agraphinfo_t", sizeof(Agraphinfo_t), true);
1047 edge_t **edgelist = gv_calloc(ecnt, sizeof(edge_t*));
1048 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
1049 for (e = agfstout(g, n); e; e = agnxtout(g, e)) {
1050 if (ED_compound(e))
1051 edgelist[i++] = e;
1052 }
1053 }
1054 assert(i == ecnt);
1055 for (i = 0; i < ecnt; i++)
1056 undoCompound(edgelist[i], clg);
1057 free (edgelist);
1058 for (n = agfstnode(clg); n; n = nextn) {
1059 nextn = agnxtnode(clg, n);
1060 gv_cleanup_node(n);
1061 agdelete(g, n);
1062 }
1063 agclose(clg);
1064}
1065
1070attrsym_t *safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue) {
1071 attrsym_t *a = agattr(g,obj_kind,name, NULL);
1072 if (!a) /* attribute does not exist */
1073 a = agattr(g, obj_kind, name, defaultValue);
1074 return a;
1075}
1076
1077static int comp_entities(const void *e1, const void *e2) {
1078 return strcmp(e1, ((const struct entities_s *)e2)->name);
1079}
1080
1081#define MAXENTLEN 8
1082
1087char* scanEntity (char* t, agxbuf* xb)
1088{
1089 char* endp = strchr (t, ';');
1090 struct entities_s *res;
1091 size_t len;
1092 char buf[MAXENTLEN+1];
1093
1094 agxbputc(xb, '&');
1095 if (!endp) return t;
1096 if ((len = (size_t)(endp - t)) > MAXENTLEN || len < 2) return t;
1097 strncpy (buf, t, len);
1098 buf[len] = '\0';
1099 res = bsearch(buf, entities, NR_OF_ENTITIES,
1100 sizeof(entities[0]), comp_entities);
1101 if (!res) return t;
1102 agxbprint(xb, "#%d;", res->value);
1103 return endp + 1;
1104}
1105
1112static int
1114{
1115 char *p;
1116 struct entities_s *res;
1117 char entity_name_buf[ENTITY_NAME_LENGTH_MAX+1];
1118 unsigned char* str = *(unsigned char**)s;
1119 unsigned int byte;
1120 int i, n = 0;
1121
1122 byte = *str;
1123 if (byte == '#') {
1124 byte = *(str + 1);
1125 if (byte == 'x' || byte == 'X') {
1126 for (i = 2; i < 8; i++) {
1127 byte = *(str + i);
1128 if (byte >= 'A' && byte <= 'F')
1129 byte = byte - 'A' + 10;
1130 else if (byte >= 'a' && byte <= 'f')
1131 byte = byte - 'a' + 10;
1132 else if (byte >= '0' && byte <= '9')
1133 byte = byte - '0';
1134 else
1135 break;
1136 n = n * 16 + (int)byte;
1137 }
1138 }
1139 else {
1140 for (i = 1; i < 8; i++) {
1141 byte = *(str + i);
1142 if (byte >= '0' && byte <= '9')
1143 n = n * 10 + ((int)byte - '0');
1144 else
1145 break;
1146 }
1147 }
1148 if (byte == ';') {
1149 str += i+1;
1150 }
1151 else {
1152 n = 0;
1153 }
1154 }
1155 else {
1156 p = entity_name_buf;
1157 for (i = 0; i < ENTITY_NAME_LENGTH_MAX; i++) {
1158 byte = *(str + i);
1159 if (byte == '\0') break;
1160 if (byte == ';') {
1161 *p++ = '\0';
1162 res = bsearch(entity_name_buf, entities, NR_OF_ENTITIES,
1163 sizeof(entities[0]), comp_entities);
1164 if (res) {
1165 n = res->value;
1166 str += i+1;
1167 }
1168 break;
1169 }
1170 *p++ = (char)byte;
1171 }
1172 }
1173 *s = (char*)str;
1174 return n;
1175}
1176
1177static unsigned char
1178cvtAndAppend (unsigned char c, agxbuf* xb)
1179{
1180 char buf[2];
1181
1182 buf[0] = c;
1183 buf[1] = '\0';
1184
1185 char *s = latin1ToUTF8(buf);
1186 char *p = s;
1187 size_t len = strlen(s);
1188 while (len-- > 1)
1189 agxbputc(xb, *p++);
1190 c = *p;
1191 free (s);
1192 return c;
1193}
1194
1199char* htmlEntityUTF8 (char* s, graph_t* g)
1200{
1201 static graph_t* lastg;
1202 static bool warned;
1203 unsigned char c;
1204 unsigned int v;
1205
1206 int uc;
1207 int ui;
1208
1209 if (lastg != g) {
1210 lastg = g;
1211 warned = false;
1212 }
1213
1214 agxbuf xb = {0};
1215
1216 while ((c = *(unsigned char*)s++)) {
1217 if (c < 0xC0)
1218 /*
1219 * Handles properly formed UTF-8 characters between
1220 * 0x01 and 0x7F. Also treats \0 and naked trail
1221 * bytes 0x80 to 0xBF as valid characters representing
1222 * themselves.
1223 */
1224 uc = 0;
1225 else if (c < 0xE0)
1226 uc = 1;
1227 else if (c < 0xF0)
1228 uc = 2;
1229 else if (c < 0xF8)
1230 uc = 3;
1231 else {
1232 uc = -1;
1233 if (!warned) {
1234 agwarningf("UTF8 codes > 4 bytes are not currently supported (graph %s) - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", agnameof(g));
1235 warned = true;
1236 }
1237 c = cvtAndAppend (c, &xb);
1238 }
1239
1240 if (uc == 0 && c == '&') {
1241 /* replace html entity sequences like: &amp;
1242 * and: &#123; with their UTF8 equivalents */
1243 v = htmlEntity (&s);
1244 if (v) {
1245 if (v < 0x7F) /* entity needs 1 byte in UTF8 */
1246 c = v;
1247 else if (v < 0x07FF) { /* entity needs 2 bytes in UTF8 */
1248 agxbputc(&xb, (char)((v >> 6) | 0xC0));
1249 c = (v & 0x3F) | 0x80;
1250 }
1251 else { /* entity needs 3 bytes in UTF8 */
1252 agxbputc(&xb, (char)((v >> 12) | 0xE0));
1253 agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
1254 c = (v & 0x3F) | 0x80;
1255 }
1256 }
1257 }
1258 else /* copy n byte UTF8 characters */
1259 for (ui = 0; ui < uc; ++ui)
1260 if ((*s & 0xC0) == 0x80) {
1261 agxbputc(&xb, (char)c);
1262 c = *(unsigned char*)s++;
1263 }
1264 else {
1265 if (!warned) {
1266 agwarningf("Invalid %d-byte UTF8 found in input of graph %s - treated as Latin-1. Perhaps \"-Gcharset=latin1\" is needed?\n", uc + 1, agnameof(g));
1267 warned = true;
1268 }
1269 c = cvtAndAppend (c, &xb);
1270 break;
1271 }
1272 agxbputc(&xb, (char)c);
1273 }
1274 return agxbdisown(&xb);
1275}
1276
1278char* latin1ToUTF8 (char* s)
1279{
1280 agxbuf xb = {0};
1281 unsigned int v;
1282
1283 /* Values are either a byte (<= 256) or come from htmlEntity, whose
1284 * values are all less than 0x07FF, so we need at most 3 bytes.
1285 */
1286 while ((v = *(unsigned char*)s++)) {
1287 if (v == '&') {
1288 v = htmlEntity (&s);
1289 if (!v) v = '&';
1290 }
1291 if (v < 0x7F)
1292 agxbputc(&xb, (char)v);
1293 else if (v < 0x07FF) {
1294 agxbputc(&xb, (char)((v >> 6) | 0xC0));
1295 agxbputc(&xb, (char)((v & 0x3F) | 0x80));
1296 }
1297 else {
1298 agxbputc(&xb, (char)((v >> 12) | 0xE0));
1299 agxbputc(&xb, (char)(((v >> 6) & 0x3F) | 0x80));
1300 agxbputc(&xb, (char)((v & 0x3F) | 0x80));
1301 }
1302 }
1303 return agxbdisown(&xb);
1304}
1305
1310char*
1312{
1313 agxbuf xb = {0};
1314 unsigned char c;
1315
1316 while ((c = *(unsigned char*)s++)) {
1317 if (c < 0x7F)
1318 agxbputc(&xb, (char)c);
1319 else {
1320 unsigned char outc = (c & 0x03) << 6;
1321 c = *(unsigned char *)s++;
1322 outc = outc | (c & 0x3F);
1323 agxbputc(&xb, (char)outc);
1324 }
1325 }
1326 return agxbdisown(&xb);
1327}
1328
1330 if (! OVERLAP(b, ND_bb(n)))
1331 return false;
1332
1333 /* FIXME - need to do something better about CLOSEENOUGH */
1334 pointf p = sub_pointf(ND_coord(n), mid_pointf(b.UR, b.LL));
1335
1336 inside_t ictxt = {.s.n = n};
1337
1338 return ND_shape(n)->fns->insidefn(&ictxt, p);
1339}
1340
1342{
1343 pointf s;
1344 s.x = lp->dimen.x / 2.;
1345 s.y = lp->dimen.y / 2.;
1346 boxf bb = {.LL = sub_pointf(lp->pos, s), .UR = add_pointf(lp->pos, s)};
1347 return OVERLAP(b, bb);
1348}
1349
1350static bool overlap_arrow(pointf p, pointf u, double scale, boxf b)
1351{
1352 // FIXME - check inside arrow shape
1353 return OVERLAP(b, arrow_bb(p, u, scale));
1354}
1355
1356static bool overlap_bezier(bezier bz, boxf b) {
1357 assert(bz.size);
1358 pointf u = bz.list[0];
1359 for (size_t i = 1; i < bz.size; i++) {
1360 pointf p = bz.list[i];
1361 if (lineToBox(p, u, b) != -1)
1362 return true;
1363 u = p;
1364 }
1365
1366 /* check arrows */
1367 if (bz.sflag) {
1368 if (overlap_arrow(bz.sp, bz.list[0], 1, b))
1369 return true;
1370 }
1371 if (bz.eflag) {
1372 if (overlap_arrow(bz.ep, bz.list[bz.size - 1], 1, b))
1373 return true;
1374 }
1375 return false;
1376}
1377
1379{
1380 splines *spl = ED_spl(e);
1381 if (spl && boxf_overlap(spl->bb, b))
1382 for (size_t i = 0; i < spl->size; i++)
1383 if (overlap_bezier(spl->list[i], b))
1384 return true;
1385
1386 textlabel_t *lp = ED_label(e);
1387 if (lp && overlap_label(lp, b))
1388 return true;
1389
1390 return false;
1391}
1392
1394static int edgeType(const char *s, int defaultValue) {
1395 if (s == NULL || strcmp(s, "") == 0) {
1396 return defaultValue;
1397 }
1398
1399 if (*s == '0') { /* false */
1400 return EDGETYPE_LINE;
1401 } else if (*s >= '1' && *s <= '9') { /* true */
1402 return EDGETYPE_SPLINE;
1403 } else if (strcasecmp(s, "curved") == 0) {
1404 return EDGETYPE_CURVED;
1405 } else if (strcasecmp(s, "compound") == 0) {
1406 return EDGETYPE_COMPOUND;
1407 } else if (strcasecmp(s, "false") == 0) {
1408 return EDGETYPE_LINE;
1409 } else if (strcasecmp(s, "line") == 0) {
1410 return EDGETYPE_LINE;
1411 } else if (strcasecmp(s, "none") == 0) {
1412 return EDGETYPE_NONE;
1413 } else if (strcasecmp(s, "no") == 0) {
1414 return EDGETYPE_LINE;
1415 } else if (strcasecmp(s, "ortho") == 0) {
1416 return EDGETYPE_ORTHO;
1417 } else if (strcasecmp(s, "polyline") == 0) {
1418 return EDGETYPE_PLINE;
1419 } else if (strcasecmp(s, "spline") == 0) {
1420 return EDGETYPE_SPLINE;
1421 } else if (strcasecmp(s, "true") == 0) {
1422 return EDGETYPE_SPLINE;
1423 } else if (strcasecmp(s, "yes") == 0) {
1424 return EDGETYPE_SPLINE;
1425 }
1426
1427 agwarningf("Unknown \"splines\" value: \"%s\" - ignored\n", s);
1428 return defaultValue;
1429}
1430
1443void setEdgeType(graph_t *g, int defaultValue) {
1444 char* s = agget(g, "splines");
1445 int et;
1446
1447 if (!s) {
1448 et = defaultValue;
1449 }
1450 else if (*s == '\0') {
1451 et = EDGETYPE_NONE;
1452 } else {
1453 et = edgeType(s, defaultValue);
1454 }
1455 GD_flags(g) |= et;
1456}
1457
1466void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags) {
1467 pointf min,max,center;
1468 int isRadial = flags & 1;
1469 int isRHS = flags & 2;
1470
1471 if (n == 2) {
1472 double rx = A[1].x - A[0].x;
1473 double ry = A[1].y - A[0].y;
1474 min.x = A[0].x - rx;
1475 max.x = A[0].x + rx;
1476 min.y = A[0].y - ry;
1477 max.y = A[0].y + ry;
1478 }
1479 else {
1480 min.x = max.x = A[0].x;
1481 min.y = max.y = A[0].y;
1482 for (size_t i = 0; i < n; i++) {
1483 min.x = MIN(A[i].x, min.x);
1484 min.y = MIN(A[i].y, min.y);
1485 max.x = MAX(A[i].x, max.x);
1486 max.y = MAX(A[i].y, max.y);
1487 }
1488 }
1489 center.x = min.x + (max.x - min.x)/2;
1490 center.y = min.y + (max.y - min.y)/2;
1491 if (isRadial) {
1492 double inner_r, outer_r;
1493 outer_r = hypot(center.x - min.x, center.y - min.y);
1494 inner_r = outer_r /4.;
1495 if (isRHS) {
1496 G[0].y = center.y;
1497 }
1498 else {
1499 G[0].y = -center.y;
1500 }
1501 G[0].x = center.x;
1502 G[1].x = inner_r;
1503 G[1].y = outer_r;
1504 }
1505 else {
1506 double half_x = max.x - center.x;
1507 double half_y = max.y - center.y;
1508 double sina = sin(angle);
1509 double cosa = cos(angle);
1510 if (isRHS) {
1511 G[0].y = center.y - half_y * sina;
1512 G[1].y = center.y + half_y * sina;
1513 }
1514 else {
1515 G[0].y = -center.y + (max.y - center.y) * sin(angle);
1516 G[1].y = -center.y - (center.y - min.y) * sin(angle);
1517 }
1518 G[0].x = center.x - half_x * cosa;
1519 G[1].x = center.x + half_x * cosa;
1520 }
1521}
1522
1524 if (ED_spl(e)) {
1525 for (size_t i = 0; i < ED_spl(e)->size; i++)
1526 free(ED_spl(e)->list[i].list);
1527 free(ED_spl(e)->list);
1528 free(ED_spl(e));
1529 }
1530 ED_spl(e) = NULL;
1531}
1532
1534{
1535 free(ED_path(e).ps);
1536 gv_free_splines(e);
1537 free_label(ED_label(e));
1541 /*FIX HERE , shallow cleaning may not be enough here */
1542 agdelrec(e, "Agedgeinfo_t");
1543}
1544
1546{
1547 free(ND_pos(n));
1548 if (ND_shape(n))
1549 ND_shape(n)->fns->freefn(n);
1550 free_label(ND_label(n));
1552 /*FIX HERE , shallow cleaning may not be enough here */
1553 agdelrec(n, "Agnodeinfo_t");
1554}
1555
1556void gv_nodesize(node_t *n, bool flip) {
1557 if (flip) {
1558 double w = INCH2PS(ND_height(n));
1559 ND_lw(n) = ND_rw(n) = w / 2;
1560 ND_ht(n) = INCH2PS(ND_width(n));
1561 }
1562 else {
1563 double w = INCH2PS(ND_width(n));
1564 ND_lw(n) = ND_rw(n) = w / 2;
1565 ND_ht(n) = INCH2PS(ND_height(n));
1566 }
1567}
1568
1569#ifndef HAVE_DRAND48
1570double drand48(void)
1571{
1572 double d;
1573 d = rand();
1574 d = d / RAND_MAX;
1575 return d;
1576}
1577#endif
1578typedef struct {
1580 char* name;
1582} clust_t;
1583
1585 .key = offsetof(clust_t, name),
1586 .size = -1,
1587 .link = offsetof(clust_t, link),
1588 .freef = free,
1589};
1590
1591static void fillMap (Agraph_t* g, Dt_t* map)
1592{
1593 for (int c = 1; c <= GD_n_cluster(g); c++) {
1594 Agraph_t *cl = GD_clust(g)[c];
1595 char *s = agnameof(cl);
1596 if (dtmatch(map, s)) {
1597 agwarningf("Two clusters named %s - the second will be ignored\n", s);
1598 } else {
1599 clust_t *ip = gv_alloc(sizeof(clust_t));
1600 ip->name = s;
1601 ip->clp = cl;
1602 dtinsert (map, ip);
1603 }
1604 fillMap (cl, map);
1605 }
1606}
1607
1613{
1614 Dt_t* map = dtopen (&strDisc, Dtoset);
1615
1616 fillMap (g, map);
1617
1618 return map;
1619}
1620
1621Agraph_t*
1622findCluster (Dt_t* map, char* name)
1623{
1624 clust_t* clp = dtmatch (map, name);
1625 if (clp)
1626 return clp->clp;
1627 return NULL;
1628}
1629
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:77
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:213
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:256
static char * agxbuse(agxbuf *xb)
Definition agxbuf.h:286
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:299
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
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 MIN(a, b)
Definition arith.h:28
boxf arrow_bb(pointf p, pointf u, double arrowsize)
Definition arrows.c:1117
void *(* Dtmake_f)(void *, Dtdisc_t *)
Definition cdt.h:50
#define dtmatch(d, o)
Definition cdt.h:192
#define dtinsert(d, o)
Definition cdt.h:193
CDT_API int dtclose(Dt_t *)
Definition dtclose.c:8
CDT_API Dtmethod_t * Dtoset
ordered set (self-adjusting tree)
Definition dttree.c:304
CDT_API Dt_t * dtopen(Dtdisc_t *, Dtmethod_t *)
Definition dtopen.c:9
pointf Bezier(pointf *V, double t, pointf *Left, pointf *Right)
Definition utils.c:169
void processClusterEdges(graph_t *g)
Definition utils.c:936
void undoClusterEdges(graph_t *g)
Definition utils.c:1035
char * late_nnstring(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:84
char * scanEntity(char *t, agxbuf *xb)
Definition utils.c:1087
#define W_DEGREE
Definition utils.c:161
static edge_t * cloneEdge(edge_t *e, node_t *ct, node_t *ch)
Make a copy of e in e's graph but using ct and ch as nodes.
Definition utils.c:791
bool mapbool(const char *p)
Definition utils.c:336
node_t * UF_union(node_t *u, node_t *v)
Definition utils.c:109
node_t * UF_find(node_t *n)
Definition utils.c:99
static int Tflag
Definition utils.c:401
static node_t * mapN(node_t *n, graph_t *clg)
Definition utils.c:978
Dt_t * mkClustMap(Agraph_t *g)
Definition utils.c:1612
void UF_setname(node_t *u, node_t *v)
Definition utils.c:144
static void undoCompound(edge_t *e, graph_t *clg)
Definition utils.c:1005
void setEdgeType(graph_t *g, int defaultValue)
Definition utils.c:1443
static port chkPort(port(*pf)(node_t *, char *, char *), node_t *n, char *s)
Definition utils.c:485
char * late_string(void *obj, attrsym_t *attr, char *defaultValue)
Definition utils.c:78
void gv_free_splines(edge_t *e)
Definition utils.c:1523
boxf polyBB(polygon_t *poly)
Definition utils.c:600
int late_int(void *obj, attrsym_t *attr, int defaultValue, int minimum)
Definition utils.c:33
static Dtdisc_t mapDisc
Definition utils.c:781
void gv_cleanup_edge(edge_t *e)
Definition utils.c:1533
static void insertEdge(Dt_t *map, void *t, void *h, edge_t *e)
Definition utils.c:802
void common_init_node(node_t *n)
Definition utils.c:422
bool overlap_label(textlabel_t *lp, boxf b)
Definition utils.c:1341
static void * newItem(item *objp, Dtdisc_t *disc)
Definition utils.c:769
int maptoken(char *p, char **name, int *val)
Definition utils.c:310
double late_double(void *obj, attrsym_t *attr, double defaultValue, double minimum)
Definition utils.c:48
static char * findPath(const strview_t *dirs, const char *str)
Definition utils.c:247
static void initFontLabelEdgeAttr(edge_t *e, struct fontinfo *fi, struct fontinfo *lfi)
Definition utils.c:460
bool overlap_node(node_t *n, boxf b)
Definition utils.c:1329
static int comp_entities(const void *e1, const void *e2)
Definition utils.c:1077
void common_init_edge(edge_t *e)
Definition utils.c:505
static bool noClip(edge_t *e, attrsym_t *sym)
Return true if head/tail end of edge should not be clipped to node.
Definition utils.c:471
const char * safefile(const char *filename)
Definition utils.c:259
pointf dotneato_closest(splines *spl, pointf pt)
Definition utils.c:341
static strview_t * mkDirlist(const char *list)
Definition utils.c:235
static bool overlap_bezier(bezier bz, boxf b)
Definition utils.c:1356
pointf coord(node_t *n)
Definition utils.c:151
attrsym_t * safe_dcl(graph_t *g, int obj_kind, char *name, char *defaultValue)
Definition utils.c:1070
void UF_singleton(node_t *u)
Definition utils.c:137
static void initFontEdgeAttr(edge_t *e, struct fontinfo *fi)
Definition utils.c:452
char * utf8ToLatin1(char *s)
Definition utils.c:1311
static int cmpItem(void *pp1, void *pp2)
Definition utils.c:755
char * latin1ToUTF8(char *s)
Converts string from Latin1 encoding to utf8. Also translates HTML entities.
Definition utils.c:1278
double get_inputscale(graph_t *g)
Definition utils.c:71
static int edgeType(const char *s, int defaultValue)
Convert string to edge type.
Definition utils.c:1394
#define MAXENTLEN
Definition utils.c:1081
static boxf addLabelBB(boxf bb, textlabel_t *lp, bool flipxy)
Definition utils.c:565
void updateBB(graph_t *g, textlabel_t *lp)
Definition utils.c:620
static bool overlap_arrow(pointf p, pointf u, double scale, boxf b)
Definition utils.c:1350
static int checkCompound(edge_t *e, graph_t *clg, agxbuf *xb, Dt_t *map, Dt_t *cmap)
Definition utils.c:852
bool overlap_edge(edge_t *e, boxf b)
Definition utils.c:1378
static unsigned char cvtAndAppend(unsigned char c, agxbuf *xb)
Definition utils.c:1178
void compute_bb(graph_t *g)
Definition utils.c:629
static void fillMap(Agraph_t *g, Dt_t *map)
Definition utils.c:1591
static Dtdisc_t strDisc
Definition utils.c:1584
static node_t * clustNode(node_t *n, graph_t *cg, agxbuf *xb, graph_t *clg)
Definition utils.c:725
void gv_cleanup_node(node_t *n)
Definition utils.c:1545
bool mapBool(const char *p, bool defaultValue)
Definition utils.c:320
char * htmlEntityUTF8(char *s, graph_t *g)
Definition utils.c:1199
static int num_clust_edges(graph_t *g)
Definition utils.c:922
#define PATHSEP
Definition utils.c:232
void get_gradient_points(pointf *A, pointf *G, size_t n, double angle, int flags)
Definition utils.c:1466
void gv_nodesize(node_t *n, bool flip)
Definition utils.c:1556
int test_toggle(void)
Definition utils.c:411
static item * mapEdge(Dt_t *map, edge_t *e)
Check if we already have cluster edge corresponding to t->h, and return it.
Definition utils.c:820
static int htmlEntity(char **s)
Definition utils.c:1113
bool is_a_cluster(Agraph_t *g)
Definition utils.c:691
static graph_t * mapc(Dt_t *cmap, node_t *n)
Definition utils.c:844
bool late_bool(void *obj, attrsym_t *attr, bool defaultValue)
Definition utils.c:91
Agraph_t * findCluster(Dt_t *map, char *name)
Definition utils.c:1622
double drand48(void)
Definition utils.c:1570
Agsym_t * setAttr(graph_t *g, void *obj, char *name, char *value, Agsym_t *ap)
Definition utils.c:700
#define HEAD_LABEL
Definition const.h:177
#define NORMAL
Definition const.h:24
#define EDGETYPE_SPLINE
Definition const.h:253
#define EDGE_XLABEL
Definition const.h:181
#define DEFAULT_NODEHEIGHT
Definition const.h:72
#define TAIL_LABEL
Definition const.h:178
#define EDGE_LABEL
Definition const.h:176
#define EDGETYPE_CURVED
Definition const.h:250
#define DEFAULT_COLOR
Definition const.h:48
#define DEFAULT_NODEWIDTH
Definition const.h:74
#define LT_RECD
Definition const.h:239
#define EDGETYPE_ORTHO
Definition const.h:252
#define MIN_NODEWIDTH
Definition const.h:75
#define DEFAULT_FONTSIZE
Definition const.h:61
#define MIN_FONTSIZE
Definition const.h:63
#define EDGETYPE_PLINE
Definition const.h:251
#define EDGETYPE_LINE
Definition const.h:249
#define LT_NONE
Definition const.h:237
#define DEFAULT_FONTNAME
Definition const.h:67
#define EDGETYPE_NONE
Definition const.h:248
#define EDGETYPE_COMPOUND
Definition const.h:254
#define DEFAULT_NODESHAPE
Definition const.h:76
#define NODE_XLABEL
Definition const.h:180
#define LT_HTML
Definition const.h:238
#define MIN_NODEHEIGHT
Definition const.h:73
#define ENTITY_NAME_LENGTH_MAX
Definition entities.h:274
static struct entities_s entities[]
#define NR_OF_ENTITIES
Definition entities.h:275
disc key
Definition exparse.y:214
#define A(n, t)
Definition expr.h:76
static int flags
Definition gc.c:61
#define G
Definition gdefs.h:7
#define V
Definition gdefs.h:5
int lineToBox(pointf p, pointf q, boxf b)
Definition geom.c:47
#define B2BF(b, bf)
Definition geom.h:75
#define OVERLAP(b0, b1)
Definition geom.h:48
#define EXPANDBP(b, p)
Definition geom.h:54
struct pointf_s pointf
#define EXPANDBB(b0, b1)
Definition geom.h:57
#define DIST2(p, q)
Definition geom.h:61
#define POINTS_PER_INCH
Definition geom.h:64
#define INCH2PS(a_inches)
Definition geom.h:69
static pointf mid_pointf(pointf p, pointf q)
Definition geomprocs.h:81
static pointf add_pointf(pointf p, pointf q)
Definition geomprocs.h:63
static pointf sub_pointf(pointf p, pointf q)
Definition geomprocs.h:72
static pointf scale(double c, pointf p)
Definition geomprocs.h:130
static bool boxf_overlap(boxf b0, boxf b1)
Definition geomprocs.h:117
Agsym_t * N_fontsize
Definition globals.h:84
Agsym_t * E_labelfontsize
Definition globals.h:103
Agsym_t * E_fontcolor
Definition globals.h:96
Agsym_t * N_width
Definition globals.h:79
Agsym_t * E_headclip
Definition globals.h:105
Agsym_t * E_headlabel
Definition globals.h:102
Agsym_t * N_showboxes
Definition globals.h:85
Agsym_t * N_fontname
Definition globals.h:84
Agsym_t * E_fontname
Definition globals.h:96
Agsym_t * N_style
Definition globals.h:85
char * HTTPServerEnVar
Definition globals.h:55
char * Gvimagepath
Definition globals.h:51
Agsym_t * E_label
Definition globals.h:97
double PSinputscale
Definition globals.h:58
char * Gvfilepath
Definition globals.h:50
Agsym_t * N_shape
Definition globals.h:79
Agsym_t * N_xlabel
Definition globals.h:85
Agsym_t * E_label_float
Definition globals.h:99
Agsym_t * E_taillabel
Definition globals.h:102
Agsym_t * N_label
Definition globals.h:85
Agsym_t * E_fontsize
Definition globals.h:96
Agsym_t * E_labelfontname
Definition globals.h:103
Agsym_t * E_xlabel
Definition globals.h:97
Agsym_t * N_fontcolor
Definition globals.h:84
Agsym_t * E_labelfontcolor
Definition globals.h:103
Agsym_t * E_tailclip
Definition globals.h:105
Agsym_t * N_height
Definition globals.h:79
static double len(glCompPoint p)
Definition glutils.c:150
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:149
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:199
int agnnodes(Agraph_t *g)
Definition graph.c:158
Agsym_t * agattr(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up attributes of a graph
Definition attr.c:341
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:356
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:481
char * agget(void *obj, char *name)
Definition attr.c:442
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:458
int agcopyattr(void *oldobj, void *newobj)
copies all of the attributes from one object to another
Definition attr.c:552
#define ED_compound(e)
Definition types.h:583
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define ED_xlabel(e)
Definition types.h:590
#define ED_label_ontop(e)
Definition types.h:591
#define ED_head_label(e)
Definition types.h:587
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define ED_spl(e)
Definition types.h:595
#define agtail(e)
Definition cgraph.h:889
#define ED_path(e)
Definition types.h:593
#define agfindedge(g, t, h)
Definition types.h:609
#define ED_tail_label(e)
Definition types.h:596
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
#define ED_head_port(e)
Definition types.h:588
#define ED_label(e)
Definition types.h:589
#define ED_tail_port(e)
Definition types.h:597
void agwarningf(const char *fmt,...)
Definition agerror.c:173
#define agfindgraphattr(g, a)
Definition types.h:613
#define GD_has_labels(g)
Definition types.h:368
#define GD_clust(g)
Definition types.h:360
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
#define GD_flags(g)
Definition types.h:365
#define GD_bb(g)
Definition types.h:354
#define GD_n_cluster(g)
Definition types.h:389
#define GD_label(g)
Definition types.h:374
#define GD_flip(g)
Definition types.h:378
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
#define ND_ht(n)
Definition types.h:500
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
#define ND_bb(n)
Definition types.h:488
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_showboxes(n)
Definition types.h:530
#define ND_has_port(n)
Definition types.h:495
#define ND_ysize(n)
Definition types.h:538
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:261
#define ND_label(n)
Definition types.h:502
#define ND_rw(n)
Definition types.h:525
#define ND_height(n)
Definition types.h:498
#define ND_width(n)
Definition types.h:536
#define ND_lw(n)
Definition types.h:506
#define ND_UF_parent(n)
Definition types.h:485
#define ND_xlabel(n)
Definition types.h:503
#define ND_UF_size(n)
Definition types.h:487
#define ND_pos(n)
Definition types.h:520
#define ND_ranktype(n)
Definition types.h:524
#define agfindnode(g, n)
Definition types.h:611
#define ND_coord(n)
Definition types.h:490
#define ND_shape(n)
Definition types.h:528
#define ND_xsize(n)
Definition types.h:537
Agraph_t * agraphof(void *obj)
Definition obj.c:184
#define AGDATA(obj)
returns Agrec_t
Definition cgraph.h:227
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:19
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:234
int agobjkind(void *obj)
Definition obj.c:253
Agraph_t * agroot(void *obj)
Definition obj.c:167
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:88
int agdelrec(void *obj, const char *name)
deletes a named record from one object
Definition rec.c:136
int aghtmlstr(const char *)
Definition refstr.c:163
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:57
void gvToggle(int s)
Definition utils.c:402
replacements for ctype.h functions
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
Arithmetic helper functions.
static bool is_exactly_zero(double v)
is a value precisely 0.0?
Definition gv_math.h:62
static int imin(int a, int b)
minimum of two integers
Definition gv_math.h:27
Graphviz context library.
#define DIRSEP
Definition gvcint.h:163
static void freef(void *ident)
static bool onetime
agxbuf * str
Definition htmlparse.c:97
textlabel_t * make_label(void *obj, char *str, int kind, double fontsize, char *fontname, char *fontcolor)
Definition labels.c:108
void free_label(textlabel_t *p)
Definition labels.c:199
static int * ps
Definition lu.c:51
#define IS_CLUST_NODE(n)
Definition macros.h:23
#define SET_CLUST_NODE(n)
Definition macros.h:22
#define CL_EDGE_TAG
Definition macros.h:21
#define HAS_CLUST_EDGE(g)
Definition macros.h:24
#define ND_id(n)
Definition mm2gv.c:39
NEATOPROCS_API void s1(graph_t *, node_t *)
Definition stuff.c:671
shape_kind shapeOf(node_t *)
Definition shapes.c:1902
shape_desc * bind_shape(char *name, node_t *)
Definition shapes.c:4004
static double cg(SparseMatrix A, const double *precond, int n, int dim, double *x0, double *rhs, double tol, double maxit)
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
platform abstraction for case-insensitive string functions
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
implementation of Agrec_t
Definition cgraph.h:172
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:639
char * defval
Definition cgraph.h:642
Definition cdt.h:104
int key
Definition cdt.h:89
size_t size
number of characters in the buffer
Definition agxbuf.h:57
Definition types.h:89
size_t size
Definition types.h:91
pointf sp
Definition types.h:94
pointf * list
Definition types.h:90
uint32_t eflag
Definition types.h:93
pointf ep
Definition types.h:95
uint32_t sflag
Definition types.h:92
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
Agrec_t hdr
Definition utils.c:917
int n_cluster_edges
Definition utils.c:918
Agraph_t * clp
Definition utils.c:1581
char * name
Definition utils.c:1580
Dtlink_t link
Definition utils.c:1579
int value
Definition entities.h:18
double fontsize
Definition utils.c:417
char * fontcolor
Definition utils.c:419
char * fontname
Definition utils.c:418
Definition utils.c:748
node_t * t
Definition utils.c:751
node_t * h
Definition utils.c:752
void * p[2]
Definition utils.c:750
Dtlink_t link
Definition utils.c:749
int y
Definition geom.h:27
int x
Definition geom.h:27
double x
Definition geom.h:29
double y
Definition geom.h:29
Definition types.h:48
char * name
Definition types.h:63
bezier * list
Definition types.h:99
boxf bb
Definition types.h:101
size_t size
Definition types.h:100
a non-owning string reference
Definition strview.h:20
pointf pos
Definition types.h:114
pointf dimen
Definition types.h:110
state for an in-progress string tokenization
Definition tokenize.h:36
Non-owning string references.
static point center(point vertex[], size_t n)
String tokenization.
static strview_t tok_get(const tok_t *t)
get the current token
Definition tokenize.h:76
static tok_t tok(const char *input, const char *separators)
begin tokenization of a new string
Definition tokenize.h:43
static bool tok_end(const tok_t *t)
is this tokenizer exhausted?
Definition tokenize.h:68
static void tok_next(tok_t *t)
advance to the next token in the string being scanned
Definition tokenize.h:85
#define ag_xget(x, a)
Definition types.h:606
#define TAIL_ID
Definition types.h:43
@ SH_RECORD
Definition types.h:187
#define HEAD_ID
Definition types.h:44
struct inside_t::@84 s
node_t * n
Definition types.h:160
Definition grammar.c:93
#define MAX(a, b)
Definition write.c:31
int(* pf)(void *, char *,...)
Definition xdot.c:405